This repository has been archived by the owner on Nov 16, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
49 changed files
with
3,568 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
apply plugin: 'com.android.library' | ||
|
||
android { | ||
compileSdkVersion 23 | ||
buildToolsVersion '23.0.2' | ||
|
||
defaultConfig { | ||
minSdkVersion 9 | ||
targetSdkVersion 23 | ||
versionCode 2 | ||
versionName "1.1" | ||
} | ||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
} | ||
|
||
dependencies { | ||
compile fileTree(dir: 'libs', include: ['*.jar']) | ||
// For backwards compatibility, not necessarily needed | ||
compile 'com.android.support:appcompat-v7:23.1.1' | ||
compile 'com.android.support:design:23.1.1' | ||
|
||
// Google's OAuth library for OpenID Connect | ||
// See https://code.google.com/p/google-oauth-java-client/wiki/Setup | ||
compile('com.google.oauth-client:google-oauth-client:1.21.0') { | ||
exclude group: 'com.google.http-client', module: 'google-http-client' | ||
exclude group: 'com.google.code.findbugs', module: 'jsr305' | ||
exclude group: 'com.google.http-client', module: 'google-http-client-jackson' | ||
exclude group: 'junit', module: 'junit' | ||
exclude group: 'com.google.guava', module: 'guava-jdk5' | ||
exclude group: 'org.apache.httpcomponents', module: 'httpclient' | ||
} | ||
// Google's JSON parsing, could be replaced with Jackson | ||
compile('com.google.api-client:google-api-client-gson:1.21.0') { | ||
exclude group: 'com.google.api-client', module: 'google-api-client' | ||
exclude group: 'com.google.code.findbugs', module: 'jsr305' | ||
exclude group: 'org.apache.httpcomponents', module: 'httpclient' | ||
} | ||
// For backwards compatibility, not necessarily needed | ||
compile('com.google.api-client:google-api-client-android:1.21.0') { | ||
exclude group: 'com.google.android', module: 'android' | ||
exclude group: 'com.google.code.findbugs', module: 'jsr305' | ||
exclude group: 'com.google.guava', module: 'guava-jdk5' | ||
exclude group: 'junit', module: 'junit' | ||
exclude group: 'org.apache.httpcomponents', module: 'httpclient' | ||
exclude group: 'com.google.http-client', module: 'google-http-client-jackson2' | ||
} | ||
|
||
// Easier HTTP requests, not necessarily needed | ||
compile('com.github.kevinsawicki:http-request:6.0') { | ||
exclude group: 'junit', module: 'junit' | ||
} | ||
|
||
// Encryption for Pre Loli devices | ||
compile 'com.madgag.spongycastle:core:1.54.0.0' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Add project specific ProGuard rules here. | ||
# By default, the flags in this file are appended to flags specified | ||
# in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt | ||
# You can edit the include path and order by changing the proguardFiles | ||
# directive in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# Add any project specific keep options here: | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
public *; | ||
} | ||
|
||
# Needed to keep generic types and @Key annotations accessed via reflection | ||
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault | ||
-keepclassmembers class * { | ||
@com.google.api.client.util.Key <fields>; | ||
} | ||
|
||
# Needed by google-http-client-android when linking against an older platform version | ||
-dontwarn com.google.api.client.extensions.android.** | ||
|
||
# Needed by google-api-client-android when linking against an older platform version | ||
-dontwarn com.google.api.client.googleapis.extensions.android.** | ||
|
||
# Needed by google-play-services when linking against an older platform version | ||
-dontwarn com.google.android.gms.** | ||
-dontnote com.google.android.gms.** | ||
|
||
# com.google.client.util.IOUtils references java.nio.file.Files when on Java 7+ | ||
-dontnote java.nio.file.Files, java.nio.file.Path | ||
|
||
# Suppress notes on LicensingServices | ||
-dontnote **.ILicensingService | ||
|
||
# Suppress warnings on sun.misc.Unsafe | ||
-dontnote sun.misc.Unsafe | ||
-dontwarn sun.misc.Unsafe |
13 changes: 13 additions & 0 deletions
13
oidclib/src/androidTest/java/com/lnikkila/oidc/ApplicationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.lnikkila.oidc; | ||
|
||
import android.app.Application; | ||
import android.test.ApplicationTestCase; | ||
|
||
/** | ||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> | ||
*/ | ||
public class ApplicationTest extends ApplicationTestCase<Application> { | ||
public ApplicationTest() { | ||
super(Application.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lnikkila.oidc"> | ||
|
||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> | ||
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> | ||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> | ||
<uses-permission android:name="android.permission.USE_CREDENTIALS" /> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:label="@string/app_name"> | ||
</application> | ||
|
||
</manifest> |
Binary file not shown.
196 changes: 196 additions & 0 deletions
196
oidclib/src/main/java/com/lnikkila/oidc/OIDCAccountManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package com.lnikkila.oidc; | ||
|
||
import android.accounts.Account; | ||
import android.accounts.AccountManager; | ||
import android.accounts.AccountManagerCallback; | ||
import android.accounts.AccountManagerFuture; | ||
import android.accounts.AuthenticatorException; | ||
import android.accounts.OperationCanceledException; | ||
import android.app.Activity; | ||
import android.content.Context; | ||
import android.os.Build; | ||
import android.os.Bundle; | ||
import android.util.Log; | ||
|
||
import com.google.api.client.auth.oauth2.TokenResponse; | ||
import com.google.api.client.auth.openidconnect.IdTokenResponse; | ||
import com.google.api.client.util.Preconditions; | ||
import com.lnikkila.oidc.authenticator.Authenticator; | ||
import com.lnikkila.oidc.security.AccountSensitiveDataStorageUtils; | ||
import com.lnikkila.oidc.security.UserNotAuthenticatedWrapperException; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* A layer of syntactic sugar around the AccountManager and the Accounts. | ||
* @author Camilo Montes | ||
* @since 20/01/2016. | ||
*/ | ||
public class OIDCAccountManager { | ||
|
||
private final String TAG = getClass().getSimpleName(); | ||
|
||
private final Context context; | ||
private final AccountManager manager; | ||
private final AccountSensitiveDataStorageUtils secureStorage; | ||
|
||
|
||
public OIDCAccountManager(Context context) { | ||
this.context = Preconditions.checkNotNull(context); | ||
this.manager = AccountManager.get(this.context); | ||
this.secureStorage = new AccountSensitiveDataStorageUtils(context); | ||
} | ||
|
||
public AccountManager getAccountManager() { | ||
return this.manager; | ||
} | ||
|
||
public String getAccountType() { | ||
return context.getString(R.string.account_authenticator_type); | ||
} | ||
|
||
public Account[] getAccounts() { | ||
return this.manager.getAccountsByType(getAccountType()); | ||
} | ||
|
||
public Account getAccountByName(String accountName) { | ||
if(accountName != null) { | ||
Account[] accounts = this.getAccounts(); | ||
for (Account account : accounts) { | ||
if (accountName.equals(account.name)) { | ||
return account; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public void createAccount(Activity activity, AccountManagerCallback<Bundle> callback) { | ||
this.manager.addAccount(getAccountType(), Authenticator.TOKEN_TYPE_ID, null, null, activity, callback, null); | ||
} | ||
|
||
public boolean removeAccount(String accountName) { | ||
Account account = getAccountByName(accountName); | ||
return removeAccount(account); | ||
} | ||
|
||
public boolean removeAccount(Account account) { | ||
boolean removed = false; | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1){ | ||
removed = this.manager.removeAccountExplicitly(account); | ||
} | ||
else { | ||
@SuppressWarnings("deprecation") AccountManagerFuture<Boolean> futureRemoved = this.manager.removeAccount(account, null, null); | ||
try { | ||
removed = futureRemoved.getResult(); | ||
} catch (OperationCanceledException | IOException | AuthenticatorException e) { | ||
Log.w("LogoutTask", "Coudln't remove account using pre LOLIPOP remove call"); | ||
} | ||
} | ||
return removed; | ||
} | ||
|
||
public boolean isKeyPinRequired() { | ||
boolean keyPinRequired = false; | ||
if (context != null) { | ||
keyPinRequired = context.getResources().getBoolean(R.bool.oidc_encryptKeyAskPin); | ||
} | ||
return keyPinRequired; | ||
} | ||
|
||
public void invalidateAllAccountTokens(Account account) { | ||
if(account != null) { | ||
try { | ||
String idToken = getIdToken(account.name, null); | ||
String accessToken = getAccessToken(account.name, null); | ||
String refreshToken = getRefreshToken(account.name, null); | ||
this.secureStorage.invalidateStringData(this.manager, account, idToken); | ||
this.secureStorage.invalidateStringData(this.manager, account, accessToken); | ||
this.secureStorage.invalidateStringData(this.manager, account, refreshToken); | ||
} catch (AuthenticatorException | UserNotAuthenticatedWrapperException | OperationCanceledException | IOException e) { | ||
Log.w(TAG, String.format("Could not invalidate account %1$s tokens", account.name), e); | ||
} | ||
} | ||
} | ||
|
||
public void invalidateAuthTokens(Account account) { | ||
if(account != null) { | ||
try { | ||
String idToken = getIdToken(account.name, null); | ||
String accessToken = getAccessToken(account.name, null); | ||
this.secureStorage.invalidateStringData(this.manager, account, idToken); | ||
this.secureStorage.invalidateStringData(this.manager, account, accessToken); | ||
} catch (AuthenticatorException | UserNotAuthenticatedWrapperException | OperationCanceledException | IOException e) { | ||
Log.w(TAG, String.format("Could not invalidate account %1$s tokens", account.name), e); | ||
} | ||
} | ||
} | ||
|
||
|
||
public void invalidateAccessToken(Account account) { | ||
if(account != null) { | ||
try { | ||
String accessToken = getAccessToken(account, null); | ||
this.secureStorage.invalidateStringData(this.manager, account, accessToken); | ||
} catch (AuthenticatorException | UserNotAuthenticatedWrapperException | OperationCanceledException | IOException e) { | ||
Log.w(TAG, String.format("Could not invalidate account %1$s AT", account.name), e); | ||
} | ||
} | ||
} | ||
|
||
public String getIdToken(String accountName, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
return getToken(accountName, Authenticator.TOKEN_TYPE_ID, callback); | ||
} | ||
|
||
public String getAccessToken(Account account, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
return getToken(account, Authenticator.TOKEN_TYPE_ACCESS, callback); | ||
} | ||
|
||
public String getAccessToken(String accountName, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
return getToken(accountName, Authenticator.TOKEN_TYPE_ACCESS, callback); | ||
} | ||
|
||
public String getRefreshToken(String accountName, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
return getToken(accountName, Authenticator.TOKEN_TYPE_REFRESH, callback); | ||
} | ||
|
||
private String getToken(String accountName, String tokenType, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
Account account = getAccountByName(accountName); | ||
return getToken(account, tokenType, callback); | ||
} | ||
|
||
private String getToken(Account account, String tokenType, AccountManagerCallback<Bundle> callback) | ||
throws AuthenticatorException, UserNotAuthenticatedWrapperException, OperationCanceledException, IOException { | ||
return this.secureStorage.retrieveStringData(this.manager, account, tokenType, callback); | ||
} | ||
|
||
public void saveTokens(Account account, TokenResponse tokenResponse) throws UserNotAuthenticatedWrapperException { | ||
if (tokenResponse instanceof IdTokenResponse) { | ||
saveToken(account, Authenticator.TOKEN_TYPE_ID, ((IdTokenResponse) tokenResponse).getIdToken()); | ||
} | ||
saveToken(account, Authenticator.TOKEN_TYPE_ACCESS, tokenResponse.getAccessToken()); | ||
saveToken(account, Authenticator.TOKEN_TYPE_REFRESH, tokenResponse.getRefreshToken()); | ||
} | ||
|
||
public void saveTokens(String accountName, TokenResponse tokenResponse) throws UserNotAuthenticatedWrapperException { | ||
if (tokenResponse instanceof IdTokenResponse) { | ||
saveToken(accountName, Authenticator.TOKEN_TYPE_ID, ((IdTokenResponse)tokenResponse).getIdToken()); | ||
} | ||
saveToken(accountName, Authenticator.TOKEN_TYPE_ACCESS, tokenResponse.getAccessToken()); | ||
saveToken(accountName, Authenticator.TOKEN_TYPE_REFRESH, tokenResponse.getRefreshToken()); | ||
} | ||
|
||
private void saveToken(String accountName, String tokenType, String token) throws UserNotAuthenticatedWrapperException { | ||
Account account = getAccountByName(accountName); | ||
saveToken(account, tokenType, token); | ||
} | ||
|
||
private void saveToken(Account account, String tokenType, String token) throws UserNotAuthenticatedWrapperException { | ||
this.secureStorage.storeStringData(this.manager, account, tokenType, token); | ||
} | ||
} |
Oops, something went wrong.