Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ either through custom URI scheme redirects, or App Links.
AS's that assume all clients are web-based or require clients to maintain
confidentiality of the client secrets may not work well.

From Android API 30 (R) and above, set [queries](https://developer.android.com/preview/privacy/package-visibility) in the manifest,
to enable AppAuth searching for usable installed browsers.
```xml
<manifest package="com.example.game">
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.APP_BROWSER" />
<data android:scheme="https" />
</intent>
</queries>
...
</manifest>
```

## Demo app

A demo app is contained within this repository. For instructions on how to
Expand Down Expand Up @@ -261,7 +281,7 @@ this redirect URI.

We recommend using a custom scheme based redirect URI (i.e. those of form
`my.scheme:/path`), as this is the most widely supported across all versions of
Android. To avoid conflicts with other apps, it is recommended to configure a
Android. To avoid conflicts with other apps, it is recommended to configure a
distinct scheme using "reverse domain name notation". This can either match
your service web domain (in reverse) e.g. `com.example.service` or your package
name `com.example.app` or be something completely new as long as it's distinct
Expand All @@ -273,12 +293,6 @@ else.
When a custom scheme is used, AppAuth can be easily configured to capture
all redirects using this custom scheme through a manifest placeholder:

```groovy
android.defaultConfig.manifestPlaceholders = [
'appAuthRedirectScheme': 'com.example.app'
]
```

Alternatively, the redirect URI can be directly configured by adding an
intent-filter for AppAuth's RedirectUriReceiverActivity to your
AndroidManifest.xml:
Expand Down
13 changes: 13 additions & 0 deletions app/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@

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

<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.APP_BROWSER" />
<data android:scheme="https" />
</intent>
</queries>

<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_short"
Expand Down
3 changes: 0 additions & 3 deletions app/README-Google.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,5 @@
}
```

3. Finally, replace the `appAuthRedirectScheme` manifest placeholder in `build.gradle` (for Module: app) with
`com.googleusercontent.apps.PREFIX`.

After this is done, install the app (`./gradlew :app:installDebug`). Authorizing a Google account
and retrieving user info should now work.
16 changes: 5 additions & 11 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,15 @@ with an invalid configuration.
The configuration file MUST contain a JSON object. The following properties can be specified:

- `redirect_uri` (required): The redirect URI to use for receiving the authorization response.
This can either be a custom scheme URI (com.example.app:/oauth2redirect/example-provider) or
an https app link (https://www.example.com/path). Custom scheme URIs are better supported
across all versions of Android, however many authorization server implementations require an
This can either be a custom scheme URI (com.example.app:/oauth2redirect/example-provider) or
an https app link (https://www.example.com/path). Custom scheme URIs are better supported
across all versions of Android, however many authorization server implementations require an
https URI. Consult the documentation for your authorization server.

The value specified here should match the value specified for `appAuthRedirectScheme` in the
`build.gradle` (Module: app), so that the demo app can capture the response.

- `end_session_redirect_uri` (required): The redirect URI to use for receiving the end session response.
This should be a custom scheme URI (com.example.app:/oauth2redirect/example-provider).
Consult the documentation for your authorization server.
This should be a custom scheme URI (com.example.app:/oauth2redirect/example-provider).
Consult the documentation for your authorization server.

The value specified here should match the value specified for `appAuthRedirectScheme` in the
`build.gradle` (Module: app), so that the demo app can capture the response.

NOTE: Scheme of the URI should be the same as `redirect_uri` but callback should be different.

- `authorization_scope` (required): The scope string to use for the authorization request.
Expand Down
6 changes: 0 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ android {
applicationId 'net.openid.appauthdemo'
project.archivesBaseName = 'appauth-demoapp'
vectorDrawables.useSupportLibrary = true

// Make sure this is consistent with the redirect URI used in res/raw/auth_config.json,
// or specify additional redirect URIs in AndroidManifest.xml
manifestPlaceholders = [
'appAuthRedirectScheme': 'net.openid.appauthdemo'
]
}

signingConfigs {
Expand Down
5 changes: 1 addition & 4 deletions app/java/net/openid/appauthdemo/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,7 @@ private void readConfiguration() throws InvalidConfigurationException {

if (!isRedirectUriRegistered()) {
throw new InvalidConfigurationException(
"redirect_uri is not handled by any activity in this app! "
+ "Ensure that the appAuthRedirectScheme in your build.gradle file "
+ "is correctly configured, or that an appropriate intent filter "
+ "exists in your app manifest.");
"redirect_uri is not handled by any activity in this app!");
}

if (getConfigString("discovery_uri") == null) {
Expand Down
29 changes: 11 additions & 18 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,18 @@ subprojects {

ext.verifyKeystore()

try {
def grgit = Grgit.open(currentDir: project.rootDir)
def lastCommit = grgit.head()

project.ext.versionNum = grgit.log(includes:['HEAD']).size()
project.ext.versionName = grgit.describe()
project.ext.versionDate = lastCommit.getDate()
if (project.ext.versionName == null) {
project.ext.versionName = 'DEV'
}
} catch (Exception ignored) {
project.ext.versionNum = 1
project.ext.versionName = 'DEV'
project.ext.versionDate = new Date()
}

project.ext {
minSdkVersion = 16
compileSdkVersion = 33
versionID = "0.11.2"
versionName = """$versionID-skyscanner"""

try {
def grgit = Grgit.open(currentDir: project.rootDir)
versionCode = grgit.log(includes: ['HEAD']).size()
} catch (Exception ignored) {
versionCode = 1
}

googleVersions = [
glide : '4.12.0',
Expand All @@ -60,8 +53,8 @@ project.ext {

task showVersion {
doLast {
logger.lifecycle("Version ID: " + project.versionNum)
logger.lifecycle("Version ID: " + project.versionID)
logger.lifecycle("Version Name: " + project.versionName)
logger.lifecycle("Version Date: " + project.versionDate)
logger.lifecycle("Version Code: " + project.versionCode)
}
}
2 changes: 1 addition & 1 deletion config/android-common.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ android {
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.compileSdkVersion
versionCode rootProject.versionNum
versionCode rootProject.versionCode
versionName rootProject.versionName
}
sourceSets {
Expand Down
9 changes: 1 addition & 8 deletions library/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@
android:theme="@style/Theme.AppCompat.Translucent.NoTitleBar"
android:launchMode="singleTask" />

<activity android:name=".RedirectUriReceiverActivity" 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:scheme="${appAuthRedirectScheme}"/>
</intent-filter>
</activity>
<activity android:name=".RedirectUriReceiverActivity" android:exported="true"/>
</application>

<queries>
Expand Down
1 change: 1 addition & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ apply plugin: 'maven-publish'
apply plugin: 'signing'

apply from: '../config/android-common.gradle'
apply from: 'gradle-maven-push.gradle'

group = GROUP
version = rootProject.versionName
Expand Down
75 changes: 75 additions & 0 deletions library/gradle-maven-push.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
apply plugin: 'maven-publish'

def artifactoryURL = "https://artifactory.skyscannertools.net/artifactory/infrastructure-maven"

task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
archiveClassifier = 'source'
}

afterEvaluate { project ->

project.ext.getJFrogPassword = {
if (project.hasProperty("SKYSCANNER_ARTIFACTORY_MAVEN_PASSWORD")) {
return project.property("SKYSCANNER_ARTIFACTORY_MAVEN_PASSWORD")
}
return ""
}

project.ext.getJFrogUsername = {
if (project.hasProperty("SKYSCANNER_ARTIFACTORY_MAVEN_USER")) {
return project.property("SKYSCANNER_ARTIFACTORY_MAVEN_USER")
}
return ""
}

publishing {
publications {
maven(MavenPublication) {
groupId 'net.openid'
artifactId 'appauth'
version versionName
description description

artifact bundleReleaseAar
artifact sourceJar

pom.withXml {

def dependenciesNode = asNode().appendNode('dependencies')
//Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each
project.configurations.implementation.allDependencies.each {
if (it.group != null && (it.name != null || "unspecified" == it.name) && it.version != null) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}

repositories {
maven {
url "$artifactoryURL"
credentials {
username "${project.getJFrogUsername()}"
password "${project.getJFrogPassword()}"
}
}
}
}

task checkMavenCredentials(type: Exec) {
outputs.upToDateWhen { false }
workingDir './'
commandLine(['curl',
'--silent',
'--fail',
'-I',
'-u',
"${project.getJFrogUsername()}:${project.getJFrogPassword()}",
"$artifactoryURL"])
}
}
2 changes: 1 addition & 1 deletion library/java/net/openid/appauth/AuthState.java
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ public void update(
// developer that this is unexpected.
Logger.warn(
"AuthState.update should not be called in an error state (%s), call update"
+ " with the result of the fresh authorization response first",
+ "with the result of the fresh authorization response first",
mAuthorizationException);
mAuthorizationException = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AppCompatActivity;

import net.openid.appauth.AuthorizationException.AuthorizationRequestErrors;
import net.openid.appauth.internal.Logger;
Expand Down Expand Up @@ -124,7 +123,7 @@
* {@link AuthorizationException} as appropriate.
* The AuthorizationManagementActivity finishes, removing itself from the back stack.
*/
public class AuthorizationManagementActivity extends AppCompatActivity {
public class AuthorizationManagementActivity extends Activity {

@VisibleForTesting
static final String KEY_AUTH_INTENT = "authIntent";
Expand Down Expand Up @@ -224,6 +223,11 @@ protected void onResume() {
*/

if (!mAuthorizationStarted) {
if (mAuthIntent == null) {
finish();
return;
}

try {
startActivity(mAuthIntent);
mAuthorizationStarted = true;
Expand Down
8 changes: 3 additions & 5 deletions library/java/net/openid/appauth/IdToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,9 @@ void validate(@NonNull TokenRequest tokenRequest,
// OpenID Connect Core Section 3.1.3.7. rule #10
// Validates that the issued at time is not more than +/- 10 minutes on the current
// time.
if (Math.abs(nowInSeconds - this.issuedAt) > TEN_MINUTES_IN_SECONDS) {
throw AuthorizationException.fromTemplate(GeneralErrors.ID_TOKEN_VALIDATION_ERROR,
new IdTokenException("Issued at time is more than 10 minutes "
+ "before or after the current time"));
}
// Not enforced. Time-based rules will break if users set the time in their device
// settings manually


// Only relevant for the authorization_code response type
if (GrantTypeValues.AUTHORIZATION_CODE.equals(tokenRequest.grantType)) {
Expand Down
11 changes: 2 additions & 9 deletions library/java/net/openid/appauth/RedirectUriReceiverActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

package net.openid.appauth;

import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

/**
* Activity that receives the redirect Uri sent by the OpenID endpoint. It forwards the data
Expand All @@ -24,13 +24,6 @@
* {@link android.app.PendingIntent}
* provided to {@link AuthorizationService#performAuthorizationRequest}.
*
* App developers using this library must override the `appAuthRedirectScheme`
* property in their `build.gradle` to specify the custom scheme that will be used for
* the OAuth2 redirect. If custom scheme redirect cannot be used with the identity provider
* you are integrating with, then a custom intent filter should be defined in your
* application manifest instead. For example, to handle
* `https://www.example.com/oauth2redirect`:
*
* ```xml
* <intent-filter>
* <action android:name="android.intent.action.VIEW"/>
Expand All @@ -42,7 +35,7 @@
* </intent-filter>
* ```
*/
public class RedirectUriReceiverActivity extends AppCompatActivity {
public class RedirectUriReceiverActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceBundle) {
Expand Down
9 changes: 7 additions & 2 deletions library/java/net/openid/appauth/browser/CustomTabManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,13 @@ public void onServiceDisconnected(ComponentName componentName) {
public void onCustomTabsServiceConnected(ComponentName componentName,
CustomTabsClient customTabsClient) {
Logger.debug("CustomTabsService is connected");
customTabsClient.warmup(0);
setClient(customTabsClient);
try {
customTabsClient.warmup(0);
setClient(customTabsClient);
} catch (SecurityException ex) {
Logger.error("CustomTabsService failed to warmup", ex);
setClient(null);
}
}

private void setClient(@Nullable CustomTabsClient client) {
Expand Down
Loading