-
Notifications
You must be signed in to change notification settings - Fork 462
Allow insecure HTTP requests on Android #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
255fad4
9d82377
6f8f49c
25c1b14
c4c1d99
a8095ee
0ec8e3d
9ba11a9
22277ed
b95a758
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import android.content.Context; | ||
| import android.content.Intent; | ||
| import android.net.Uri; | ||
| import android.support.annotation.NonNull; | ||
| import android.support.annotation.Nullable; | ||
|
|
||
| import com.facebook.react.bridge.ActivityEventListener; | ||
|
|
@@ -16,25 +17,37 @@ | |
| import com.facebook.react.bridge.ReadableMap; | ||
| import com.facebook.react.bridge.ReadableMapKeySetIterator; | ||
| import com.facebook.react.bridge.WritableMap; | ||
| import com.reactlibrary.utils.UnsafeConnectionBuilder; | ||
|
|
||
| import net.openid.appauth.AppAuthConfiguration; | ||
| import net.openid.appauth.AuthorizationException; | ||
| import net.openid.appauth.AuthorizationRequest; | ||
| import net.openid.appauth.AuthorizationResponse; | ||
| import net.openid.appauth.AuthorizationService; | ||
| import net.openid.appauth.AuthorizationServiceConfiguration; | ||
| import net.openid.appauth.Preconditions; | ||
| import net.openid.appauth.ResponseTypeValues; | ||
| import net.openid.appauth.TokenResponse; | ||
| import net.openid.appauth.TokenRequest; | ||
|
|
||
| import net.openid.appauth.connectivity.ConnectionBuilder; | ||
| import net.openid.appauth.connectivity.DefaultConnectionBuilder; | ||
|
|
||
| import java.io.IOException; | ||
| import java.net.HttpURLConnection; | ||
| import java.net.MalformedURLException; | ||
| import java.net.URL; | ||
| import java.sql.Connection; | ||
| import java.text.SimpleDateFormat; | ||
| import java.util.Date; | ||
| import java.util.HashMap; | ||
| import java.util.Iterator; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| public class RNAppAuthModule extends ReactContextBaseJavaModule implements ActivityEventListener { | ||
|
|
||
| private final ReactApplicationContext reactContext; | ||
| private Promise promise; | ||
| private Boolean dangerouslyAllowInsecureHttpRequests; | ||
|
|
||
| public RNAppAuthModule(ReactApplicationContext reactContext) { | ||
| super(reactContext); | ||
|
|
@@ -96,24 +109,53 @@ private HashMap<String, String> additionalParametersToMap(ReadableMap additional | |
| return additionalParametersHash; | ||
| } | ||
|
|
||
| private AppAuthConfiguration createAppAuthConfiguration(ConnectionBuilder connectionBuilder) { | ||
| return new AppAuthConfiguration | ||
| .Builder() | ||
| .setConnectionBuilder(connectionBuilder) | ||
| .build(); | ||
| } | ||
|
|
||
| private ConnectionBuilder createConnectionBuilder(Boolean allowInsecureConnections) { | ||
| if (allowInsecureConnections.equals(true)) { | ||
| return UnsafeConnectionBuilder.INSTANCE; | ||
| } | ||
|
|
||
| return DefaultConnectionBuilder.INSTANCE; | ||
| } | ||
|
|
||
| private Uri buildConfigurationUriFromIssuer(Uri openIdConnectIssuerUri) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are now using |
||
| return openIdConnectIssuerUri.buildUpon() | ||
| .appendPath(AuthorizationServiceConfiguration.WELL_KNOWN_PATH) | ||
| .appendPath(AuthorizationServiceConfiguration.OPENID_CONFIGURATION_RESOURCE) | ||
| .build(); | ||
| } | ||
|
|
||
| @ReactMethod | ||
| public void authorize( | ||
| String issuer, | ||
| final String redirectUrl, | ||
| final String clientId, | ||
| final ReadableArray scopes, | ||
| final ReadableMap additionalParameters, | ||
| final Boolean dangerouslyAllowInsecureHttpRequests, | ||
| final Promise promise | ||
| ) { | ||
|
|
||
| final Context context = this.reactContext; | ||
|
|
||
| // store args in private fields for later use in onActivityResult handler | ||
| this.promise = promise; | ||
| final Activity currentActivity = getCurrentActivity(); | ||
| this.dangerouslyAllowInsecureHttpRequests = dangerouslyAllowInsecureHttpRequests; | ||
|
|
||
| final Activity currentActivity = getCurrentActivity(); | ||
| final String scopesString = this.arrayToString(scopes); | ||
| final Uri issuerUri = Uri.parse(issuer); | ||
| final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests); | ||
| final AppAuthConfiguration configuration = this.createAppAuthConfiguration(builder); | ||
|
|
||
| AuthorizationServiceConfiguration.fetchFromIssuer( | ||
| Uri.parse(issuer), | ||
| AuthorizationServiceConfiguration.fetchFromUrl( | ||
| buildConfigurationUriFromIssuer(issuerUri), | ||
| new AuthorizationServiceConfiguration.RetrieveConfigurationCallback() { | ||
| public void onFetchConfigurationCompleted( | ||
| @Nullable AuthorizationServiceConfiguration serviceConfiguration, | ||
|
|
@@ -123,6 +165,7 @@ public void onFetchConfigurationCompleted( | |
| return; | ||
| } | ||
|
|
||
|
|
||
| AuthorizationRequest.Builder authRequestBuilder = | ||
| new AuthorizationRequest.Builder( | ||
| serviceConfiguration, | ||
|
|
@@ -137,13 +180,14 @@ public void onFetchConfigurationCompleted( | |
| } | ||
|
|
||
| AuthorizationRequest authRequest = authRequestBuilder.build(); | ||
|
|
||
| AuthorizationService authService = new AuthorizationService(context); | ||
| AuthorizationService authService = new AuthorizationService(context, configuration); | ||
| Intent authIntent = authService.getAuthorizationRequestIntent(authRequest); | ||
| currentActivity.startActivityForResult(authIntent, 0); | ||
|
|
||
| } | ||
| }); | ||
| }, | ||
| builder | ||
| ); | ||
|
|
||
| } | ||
|
|
||
|
|
@@ -155,14 +199,20 @@ public void refresh( | |
| final String refreshToken, | ||
| final ReadableArray scopes, | ||
| final ReadableMap additionalParameters, | ||
| final Boolean dangerouslyAllowInsecureHttpRequests, | ||
| final Promise promise | ||
| ) { | ||
| final Context context = this.reactContext; | ||
|
|
||
| final String scopesString = this.arrayToString(scopes); | ||
| final Uri issuerUri = Uri.parse(issuer); | ||
| final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests); | ||
| final AppAuthConfiguration configuration = createAppAuthConfiguration(builder); | ||
|
|
||
| AuthorizationServiceConfiguration.fetchFromIssuer( | ||
| Uri.parse(issuer), | ||
| // store setting in private field for later use in onActivityResult handler | ||
| this.dangerouslyAllowInsecureHttpRequests = dangerouslyAllowInsecureHttpRequests; | ||
|
|
||
| AuthorizationServiceConfiguration.fetchFromUrl( | ||
| buildConfigurationUriFromIssuer(issuerUri), | ||
| new AuthorizationServiceConfiguration.RetrieveConfigurationCallback() { | ||
| public void onFetchConfigurationCompleted( | ||
| @Nullable AuthorizationServiceConfiguration serviceConfiguration, | ||
|
|
@@ -187,9 +237,7 @@ public void onFetchConfigurationCompleted( | |
|
|
||
| TokenRequest tokenRequest = tokenRequestBuilder.build(); | ||
|
|
||
|
|
||
| AuthorizationService authService = new AuthorizationService(context); | ||
|
|
||
| AuthorizationService authService = new AuthorizationService(context, configuration); | ||
| authService.performTokenRequest(tokenRequest, new AuthorizationService.TokenResponseCallback() { | ||
| @Override | ||
| public void onTokenRequestCompleted(@Nullable TokenResponse response, @Nullable AuthorizationException ex) { | ||
|
|
@@ -203,7 +251,8 @@ public void onTokenRequestCompleted(@Nullable TokenResponse response, @Nullable | |
| }); | ||
|
|
||
| } | ||
| }); | ||
| }, | ||
| builder); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -217,9 +266,11 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, | |
| } | ||
|
|
||
| final Promise authorizePromise = this.promise; | ||
| final AppAuthConfiguration configuration = createAppAuthConfiguration( | ||
| createConnectionBuilder(this.dangerouslyAllowInsecureHttpRequests) | ||
| ); | ||
|
|
||
| AuthorizationService authService = new AuthorizationService(this.reactContext); | ||
|
|
||
| AuthorizationService authService = new AuthorizationService(this.reactContext, configuration); | ||
| authService.performTokenRequest( | ||
| response.createTokenExchangeRequest(), | ||
| new AuthorizationService.TokenResponseCallback() { | ||
|
|
@@ -248,4 +299,4 @@ public void onNewIntent(Intent intent) { | |
| public String getName() { | ||
| return "RNAppAuth"; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package com.reactlibrary.utils; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file is copied as-is from the AppAuth Android example application. Only modification here is that I changed the name from |
||
|
|
||
| /* | ||
| * Copyright 2016 The AppAuth for Android Authors. All Rights Reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
| * in compliance with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software distributed under the | ||
| * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| * express or implied. See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
|
|
||
| import android.annotation.SuppressLint; | ||
| import android.net.Uri; | ||
| import android.support.annotation.NonNull; | ||
| import android.support.annotation.Nullable; | ||
| import android.util.Log; | ||
|
|
||
| import net.openid.appauth.Preconditions; | ||
| import net.openid.appauth.connectivity.ConnectionBuilder; | ||
|
|
||
| import java.io.IOException; | ||
| import java.net.HttpURLConnection; | ||
| import java.net.URL; | ||
| import java.security.KeyManagementException; | ||
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.cert.X509Certificate; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| import javax.net.ssl.HostnameVerifier; | ||
| import javax.net.ssl.HttpsURLConnection; | ||
| import javax.net.ssl.SSLContext; | ||
| import javax.net.ssl.SSLSession; | ||
| import javax.net.ssl.TrustManager; | ||
| import javax.net.ssl.X509TrustManager; | ||
|
|
||
| /** | ||
| * An implementation of {@link ConnectionBuilder} that permits connecting to http | ||
| * links, and ignores certificates for https connections. *THIS SHOULD NOT BE USED IN PRODUCTION | ||
| * CODE*. It is intended to facilitate easier testing of AppAuth against development servers | ||
| * only. | ||
| */ | ||
| public final class UnsafeConnectionBuilder implements ConnectionBuilder { | ||
|
|
||
| public static final UnsafeConnectionBuilder INSTANCE = new UnsafeConnectionBuilder(); | ||
|
|
||
| private static final String TAG = "ConnBuilder"; | ||
|
|
||
| private static final int CONNECTION_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(15); | ||
| private static final int READ_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(10); | ||
|
|
||
| private static final String HTTP = "http"; | ||
| private static final String HTTPS = "https"; | ||
|
|
||
| @SuppressLint("TrustAllX509TrustManager") | ||
| private static final TrustManager[] ANY_CERT_MANAGER = new TrustManager[] { | ||
| new X509TrustManager() { | ||
| public X509Certificate[] getAcceptedIssuers() { | ||
| return null; | ||
| } | ||
|
|
||
| public void checkClientTrusted(X509Certificate[] certs, String authType) {} | ||
|
|
||
| public void checkServerTrusted(X509Certificate[] certs, String authType) {} | ||
| } | ||
| }; | ||
|
|
||
| @SuppressLint("BadHostnameVerifier") | ||
| private static final HostnameVerifier ANY_HOSTNAME_VERIFIER = new HostnameVerifier() { | ||
| public boolean verify(String hostname, SSLSession session) { | ||
| return true; | ||
| } | ||
| }; | ||
|
|
||
| @Nullable | ||
| private static final SSLContext TRUSTING_CONTEXT; | ||
|
|
||
| static { | ||
| SSLContext context; | ||
| try { | ||
| context = SSLContext.getInstance("SSL"); | ||
| } catch (NoSuchAlgorithmException e) { | ||
| Log.e("ConnBuilder", "Unable to acquire SSL context"); | ||
| context = null; | ||
| } | ||
|
|
||
| SSLContext initializedContext = null; | ||
| if (context != null) { | ||
| try { | ||
| context.init(null, ANY_CERT_MANAGER, new java.security.SecureRandom()); | ||
| initializedContext = context; | ||
| } catch (KeyManagementException e) { | ||
| Log.e(TAG, "Failed to initialize trusting SSL context"); | ||
| } | ||
| } | ||
|
|
||
| TRUSTING_CONTEXT = initializedContext; | ||
| } | ||
|
|
||
| private UnsafeConnectionBuilder() { | ||
| // no need to construct new instances | ||
| } | ||
|
|
||
| @NonNull | ||
| @Override | ||
| public HttpURLConnection openConnection(@NonNull Uri uri) throws IOException { | ||
| Preconditions.checkNotNull(uri, "url must not be null"); | ||
| Preconditions.checkArgument(HTTP.equals(uri.getScheme()) || HTTPS.equals(uri.getScheme()), | ||
| "scheme or uri must be http or https"); | ||
| HttpURLConnection conn = (HttpURLConnection) new URL(uri.toString()).openConnection(); | ||
| conn.setConnectTimeout(CONNECTION_TIMEOUT_MS); | ||
| conn.setReadTimeout(READ_TIMEOUT_MS); | ||
| conn.setInstanceFollowRedirects(false); | ||
|
|
||
| if (conn instanceof HttpsURLConnection && TRUSTING_CONTEXT != null) { | ||
| HttpsURLConnection httpsConn = (HttpsURLConnection) conn; | ||
| httpsConn.setSSLSocketFactory(TRUSTING_CONTEXT.getSocketFactory()); | ||
| httpsConn.setHostnameVerifier(ANY_HOSTNAME_VERIFIER); | ||
| } | ||
|
|
||
| return conn; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this what need to be done on iOS in order to support insecure connections? If so, should we add a comment to the readme on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, could do that. The library already gives you a very informative warning saying the App Transport Security settings needs to be changed to allow HTTP connections. But no harm putting it in the README to save folks some cycles. Will amend 👍