Skip to content
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

Use Chrome Custom Tabs when possible #95

Merged
merged 9 commits into from
Jul 3, 2017
69 changes: 42 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Android API version 15 or newer

## Installation

###Gradle
### Gradle

Auth0.android is available through [Gradle](https://gradle.org/). To install it, simply add the following line to your `build.gradle` file:

Expand Down Expand Up @@ -269,60 +269,75 @@ First go to [Auth0 Dashboard](https://manage.auth0.com/#/applications) and go to
https://{YOUR_AUTH0_DOMAIN}/android/{YOUR_APP_PACKAGE_NAME}/callback
```

Open your app's `AndroidManifest.xml` file and add the following permission.
Remember to replace `{YOUR_APP_PACKAGE_NAME}` with your actual application's package name, available in your `app/build.gradle` file as the `applicationId` value.

```xml
<uses-permission android:name="android.permission.INTERNET" />

Next, define a placeholder for the Auth0 Domain which is going to be used internally by the library to register an **intent-filter**. Go to your application's `build.gradle` file and add the `manifestPlaceholders` line as shown below:

```groovy
apply plugin: 'com.android.application'

android {
compileSdkVersion 25
defaultConfig {
applicationId "com.auth0.samples"
minSdkVersion 15
targetSdkVersion 25
//...

//---> Add the next line
manifestPlaceholders = [auth0Domain: "@string/auth0_domain"]
//<---
}
//...
}
```

Also register the intent filters inside your activity's tag, so you can receive the call in your activity. Note that you will have to specify the callback url inside the `data` tag.
It's a good practice to define reusable resources like `@string/auth0_domain` but you can also hard code the value in the file.

Alternatively, you can declare the `RedirectActivity` in the `AndroidManifest.xml` file with your own **intent-filter** so it overrides the library's default. If you do this then the `manifestPlaceholders` don't need to be set as long as the activity contains the `tools:node="replace"` like in the snippet below. If you choose to use a [custom scheme](#a-note-about-app-deep-linking) you must define your own intent-filter as explained below.

In your manifest inside your application's tag add the `RedirectActivity` declaration:

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.app.package">
<application android:theme="@style/AppTheme">

<!-- ... -->

<activity
android:name="com.mycompany.MainActivity"
android:theme="@style/MyAppTheme"
android:launchMode="singleTask">

<intent-filter android:autoVerify="true">
android:name="com.auth0.android.provider.RedirectActivity"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="{YOUR_AUTH0_DOMAIN}"
android:pathPrefix="/android/{YOUR_APP_PACKAGE_NAME}/callback"
android:host="@string/auth0_domain"
android:pathPrefix="/android/${applicationId}/callback"
android:scheme="https" />
</intent-filter>

</activity>

<!-- ... -->

</application>
</manifest>
```

Make sure the Activity's **launchMode** is declared as "singleTask" or the result won't come back after the authentication.
If you request a different scheme you must replace the `android:scheme` property value. Finally, don't forget to add the internet permission.

When you launch the WebAuthProvider you'll expect a result back. To capture the response override the `onNewIntent` method and call `WebAuthProvider.resume()` with the received parameters:
```xml
<uses-permission android:name="android.permission.INTERNET" />
```

```java
public class MyActivity extends Activity {

@Override
protected void onNewIntent(Intent intent) {
if (WebAuthProvider.resume(intent)) {
return;
}
super.onNewIntent(intent);
}
}
> In versions 1.8.0 and before you had to define the **intent-filter** inside your activity to capture the result in the `onNewIntent` method and call `WebAuthProvider.resume()` with the received intent. This call is no longer required for versions greater than 1.8.0 as it's now done for you by the library.

```

##### A note about App Deep Linking:

Expand Down Expand Up @@ -451,7 +466,7 @@ android {

ref: https://github.com/square/okio/issues/58#issuecomment-72672263

##Proguard
## Proguard
The rules should be applied automatically if your application is using `minifyEnabled = true`. If you want to include them manually check the [proguard directory](proguard).
By default you should at least use the following files:
* `proguard-okio.pro`
Expand Down
11 changes: 9 additions & 2 deletions auth0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,21 @@ android {
lintOptions {
warning 'InvalidPackage'
}
buildTypes {
debug {
//Helps tests. buildTypes values are not included in the merged manifest
manifestPlaceholders = [auth0Domain: "auth0.test.domain"]
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:customtabs:25.3.1'
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.google.code.gson:gson:2.7'
compile 'com.auth0.android:jwtdecode:1.1.0'

testCompile 'junit:junit:4.12'
Expand Down
33 changes: 32 additions & 1 deletion auth0/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,35 @@
~ THE SOFTWARE.
-->

<manifest package="com.auth0.android.auth0" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.auth0.android.auth0">

<uses-permission android:name="android.permission.INTERNET" />

<application>
<activity
android:name="com.auth0.android.provider.AuthenticationActivity"
android:exported="false"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<activity
android:name="com.auth0.android.provider.RedirectActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="${auth0Domain}"
android:pathPrefix="/android/${applicationId}/callback"
android:scheme="https" />
</intent-filter>
</activity>

<activity android:name="com.auth0.android.provider.WebAuthActivity" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.auth0.android.provider;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;

public class AuthenticationActivity extends Activity {

static final String EXTRA_USE_BROWSER = "com.auth0.android.EXTRA_USE_BROWSER";
static final String EXTRA_USE_FULL_SCREEN = "com.auth0.android.EXTRA_USE_FULL_SCREEN";
static final String EXTRA_CONNECTION_NAME = "com.auth0.android.EXTRA_CONNECTION_NAME";
private static final String EXTRA_INTENT_LAUNCHED = "com.auth0.android.EXTRA_INTENT_LAUNCHED";

private boolean intentLaunched;
private CustomTabsController customTabsController;

static void authenticateUsingBrowser(Context context, Uri authorizeUri) {
Intent intent = new Intent(context, AuthenticationActivity.class);
intent.setData(authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
}

static void authenticateUsingWebView(Activity activity, Uri authorizeUri, int requestCode, String connection, boolean useFullScreen) {
Intent intent = new Intent(activity, AuthenticationActivity.class);
intent.setData(authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, false);
intent.putExtra(AuthenticationActivity.EXTRA_USE_FULL_SCREEN, useFullScreen);
intent.putExtra(AuthenticationActivity.EXTRA_CONNECTION_NAME, connection);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivityForResult(intent, requestCode);
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
deliverSuccessfulAuthenticationResult(data);
}
finish();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_INTENT_LAUNCHED, intentLaunched);
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
intentLaunched = savedInstanceState.getBoolean(EXTRA_INTENT_LAUNCHED, false);
}
}

@Override
protected void onResume() {
super.onResume();
if (!intentLaunched) {
intentLaunched = true;
launchAuthenticationIntent();
return;
}

if (getIntent().getData() != null) {
deliverSuccessfulAuthenticationResult(getIntent());
}
finish();
}

@Override
protected void onDestroy() {
super.onDestroy();
if (customTabsController != null) {
customTabsController.unbindService();
customTabsController = null;
}
}

private void launchAuthenticationIntent() {
Bundle extras = getIntent().getExtras();
final Uri authorizeUri = getIntent().getData();
if (!extras.getBoolean(EXTRA_USE_BROWSER, true)) {
Intent intent = new Intent(this, WebAuthActivity.class);
intent.setData(authorizeUri);
intent.putExtra(WebAuthActivity.CONNECTION_NAME_EXTRA, extras.getString(EXTRA_CONNECTION_NAME));
intent.putExtra(WebAuthActivity.FULLSCREEN_EXTRA, extras.getBoolean(EXTRA_USE_FULL_SCREEN));
//The request code value can be ignored
startActivityForResult(intent, 33);
return;
}

customTabsController = createCustomTabsController(this);
customTabsController.bindService();
customTabsController.launchUri(authorizeUri);
}

@VisibleForTesting
CustomTabsController createCustomTabsController(@NonNull Context context) {
return new CustomTabsController(context);
}

@VisibleForTesting
void deliverSuccessfulAuthenticationResult(Intent result) {
WebAuthProvider.resume(result);
}

}
Loading