Skip to content

Commit

Permalink
Merge pull request #7 from ForgeRock/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
witrisna authored Dec 10, 2019
2 parents 928cb18 + a54ffe4 commit 46b6807
Show file tree
Hide file tree
Showing 16 changed files with 377 additions and 210 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Version 1.0.0

## [1.0.0]
- General Availability release for SDKs

#### Fixed
- Changed OAuth2 authorization request to POST [SDKS-125]
- Store SSO token even SSO is disabled [SDKS-166]

## [0.9.0]

#### Added
- Initial release for forgerock-auth sdk
- Initial release for forgerock-auth-ui sdk
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies {

| Feature | Dependency |
| ------------- |:-------------:|
| UI Template | implementation 'org.forgerock:forgerock-auth-ui:0.9.0|
| UI Template | implementation 'org.forgerock:forgerock-auth-ui:1.0.0|
| ReCAPTCHA | implementation 'com.google.android.gms:play-services-safetynet:17.0.0' |
| Location for Device Profile | implementation 'com.google.android.gms:play-services-safetynet:17.0.0' |

Expand Down
11 changes: 6 additions & 5 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
<string name="login">Login</string>


<!-- Forgerock SDK Configuration -->
<!-- ForgeRock SDK Configuration -->

<!-- OAuth -->
<string name="forgerock_oauth_client_id" translatable="false">andy_app2</string>
<string name="forgerock_oauth_redirect_uri" translatable="false">https://www.example2.com/callback</string>
<string name="forgerock_oauth_client_id" translatable="false">andy_app</string>
<string name="forgerock_oauth_redirect_uri" translatable="false">http://www.example2.com/callback</string>
<string name="forgerock_oauth_scope" translatable="false">openid profile email address phone</string>
<string name="forgerock_oauth_url" translatable="false">https://openam.example.com:8081/openam</string>
<string name="forgerock_oauth_url" translatable="false">http://openam.example.com:8081/openam</string>
<integer name="forgerock_oauth_threshold" translatable="false">30</integer> <!-- in second -->

<!-- The following setting is for local development environment only -->
<!-- Server -->
<string name="forgerock_url" translatable="false">https://openam.example.com:8081/openam</string>
<string name="forgerock_url" translatable="false">http://openam.example.com:8081/openam</string>
<string name="forgerock_realm" translatable="false">root</string>
<integer name="forgerock_timeout" translatable="false">30</integer>

Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand Down
2 changes: 1 addition & 1 deletion forgerock-auth-ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionCode 2
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
2 changes: 1 addition & 1 deletion forgerock-auth/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionCode 2
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2019 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

package org.forgerock.android.auth;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.util.Base64;

import org.forgerock.android.auth.authenticator.AuthenticatorService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

import lombok.Builder;
import lombok.NonNull;

/**
* Manage SSO Token with {@link AccountManager} as the storage.
* The stored token can be shared with SSO group Apps.
* For Android L, the encrypted {@link javax.crypto.SecretKey} will
* be stored as user's password using {@link AccountManager#setPassword(Account, String)},
* for Android M+ the SecretKey will be store in the KeyChain.
*/
class AccountSingleSignOnManager implements SingleSignOnManager, KeyUpdatedListener, SecretKeyStore {

private static final String TAG = AccountSingleSignOnManager.class.getSimpleName();
private static final String ACCOUNT_TYPE = "accountType";
private static final String ORG_FORGEROCK_V_1_SSO_KEYS = "org.forgerock.v1.SSO_KEYS";
private static final String SSO_TOKEN = "org.forgerock.v1.SSO_TOKEN";

private String accountType;
private Encryptor encryptor;
private AccountManager accountManager;
private Account account;

@Builder
public AccountSingleSignOnManager(@NonNull Context context, Encryptor encryptor) throws Exception {
Config config = Config.getInstance(context);
try {
this.accountType = getAccountType(context);
} catch (Exception e) {
//consider SSO is disabled.
Logger.warn(TAG, "Single Sign On is disabled due to: %s", e.getMessage());
throw e;
}
this.accountManager = AccountManager.get(context);
this.account = new Account(config.getAccountName(), accountType);
this.encryptor = config.applyDefaultIfNull(encryptor, context, this::getEncryptor);
Logger.debug(TAG, "Using Encryptor %s", this.encryptor.getClass().getSimpleName());
}

@Override
public void persist(SSOToken token) {
persist(token, true);
}

private void persist(SSOToken token, boolean retry) {
accountManager.addAccountExplicitly(account, null, null);
try {
accountManager.setUserData(account, SSO_TOKEN,
Base64.encodeToString(encryptor.encrypt(token.getValue().getBytes()), Base64.DEFAULT));
} catch (Exception e) {
try {
encryptor.reset();
if (retry) {
persist(token, false);
} else {
throw new RuntimeException(e);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}

@Override
public void clear() {
Account[] accounts = accountManager.getAccountsByType(accountType);
for (Account acc : accounts) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
accountManager.removeAccountExplicitly(acc);
} else {
AccountManagerFuture<Boolean> future = accountManager.removeAccount(acc, null, null);
try {
future.getResult();
} catch (Exception e) {
Logger.warn(TAG, e, "Failed to remove Account %s.", acc.name);
}
}
}
}

@Override
public SSOToken getToken() {
try {
String encryptedToken = accountManager.getUserData(account, SSO_TOKEN);
if (encryptedToken != null) {
return new SSOToken(new String(encryptor.decrypt(Base64.decode(encryptedToken, Base64.DEFAULT))));
} else {
return null;
}
} catch (Exception e) {
return null;
}

}

@Override
public boolean hasToken() {
return accountManager.getUserData(account, SSO_TOKEN) != null;
}

@Override
public void revoke(FRListener<Void> listener) {
clear();
}

private String getAccountType(Context context) throws PackageManager.NameNotFoundException, IOException, XmlPullParserException {
// Get the authenticator XML file from AndroidManifest.xml
ComponentName cn = new ComponentName(context, AuthenticatorService.class);
ServiceInfo info = context.getPackageManager().getServiceInfo(cn, PackageManager.GET_META_DATA);
int resourceId = info.metaData.getInt("android.accounts.AccountAuthenticator");

// Parse the authenticator XML file to get the accountType
return parse(context, resourceId);
}

private String parse(Context context, int resourceId) throws IOException, XmlPullParserException {
XmlResourceParser xrp = context.getResources().getXml(resourceId);
xrp.next();
int eventType = xrp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG || eventType == XmlPullParser.END_TAG) {
for (int i = 0; i < xrp.getAttributeCount(); i++) {
String name = xrp.getAttributeName(i);
if (ACCOUNT_TYPE.equals(name)) {
return xrp.getAttributeValue(i);
}
}
}
eventType = xrp.next();
}
throw new IllegalArgumentException("AccountType is not defined under forgerock_authenticator.xml");

}

@SuppressLint("NewApi")
private Encryptor getEncryptor(Context context) {
switch (Build.VERSION.SDK_INT) {
case Build.VERSION_CODES.LOLLIPOP:
case Build.VERSION_CODES.LOLLIPOP_MR1:
return new AndroidLEncryptor(context, ORG_FORGEROCK_V_1_SSO_KEYS, this);
case Build.VERSION_CODES.M:
return new AndroidMEncryptor(ORG_FORGEROCK_V_1_SSO_KEYS, this);
case Build.VERSION_CODES.N:
return new AndroidNEncryptor(ORG_FORGEROCK_V_1_SSO_KEYS, this);
default:
return new AndroidNEncryptor(ORG_FORGEROCK_V_1_SSO_KEYS, this);
}
}

@Override
public void persist(String encryptedSecretKey) {
accountManager.setPassword(account, encryptedSecretKey);
}

@Override
public String getEncryptedSecretKey() {
return accountManager.getPassword(account);
}

@Override
public void remove() {
accountManager.setPassword(account, null);
}

/**
* When the keys that use to encrypt the data are updated (Platform upgrade from Android L to Android M)
*/
@Override
public void onKeyUpdated() {
clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
import java.net.MalformedURLException;
import java.net.URL;

import static org.forgerock.android.auth.ServerConfig.ACCEPT_API_VERSION;
import static org.forgerock.android.auth.ServerConfig.API_VERSION_2_1;

/**
* Client to interact with the auth tree APIs
*/
class AuthServiceClient {

private static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
private static final String ACCEPT_API_VERSION = "Accept-API-Version";
private static final String AUTH_INDEX_TYPE = "authIndexType";
private static final String AUTH_INDEX_VALUE = "authIndexValue";
private static final String SERVICE = "service";
Expand All @@ -48,7 +50,7 @@ void authenticate(final AuthService authService, final AuthServiceResponseHandle
Request request = new Request.Builder()
.url(getUrl(authService.getName()))
.post(RequestBody.create(new byte[0]))
.header(ACCEPT_API_VERSION, ServerConfig.API_VERSION_2_1)
.header(ACCEPT_API_VERSION, API_VERSION_2_1)
.build();

okHttpClient.newCall(request).enqueue(new Callback() {
Expand Down
Loading

0 comments on commit 46b6807

Please sign in to comment.