Skip to content

Commit

Permalink
master <-- development (#139)
Browse files Browse the repository at this point in the history
* Update login widget section in readme (#133)

* Updates Server endpoints to v4 (#135)

* Changes SDK to use server v4 endpoints. Updates test cases

* 1. Adds version and AZP validation 2. Updates iss validation

* Issue 2439 (#138)

Removed azp check
  • Loading branch information
gtaban authored Apr 5, 2019
1 parent 76f1835 commit 079a75e
Show file tree
Hide file tree
Showing 23 changed files with 172 additions and 318 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ loginWidget.launch(this, new AuthorizationListener() {
```
**Note**:

* The default configuration use Facebook and Google as authentication options. If you configure only one of them the login widget will *not* launch and the user will be redirected to the configured identity provider authentication screen.
* By default, App ID is configured to use Facebook, Google, and Cloud Directory as identity providers. If you change your identity provider settings to provide only one option, then the Login Widget is not needed and will not display. The user is directed to your chosen identity provider's authentication screen.
* When using Cloud Directory, and "Email verification" is configured to *not* allow users to sign-in without email verification, then the "onAuthorizationSuccess" of the "AuthorizationListener" will be invoked without tokens.

## Managing Cloud Directory with the Android SDK
Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ buildscript {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
dependencies {
classpath "com.android.tools.build:gradle:3.1.1"
classpath 'com.android.tools.build:gradle:3.1.3'
// classpath 'com.android.tools.build:gradle:3.1.2'
classpath "org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.7.1"
// classpath 'org.robolectric:robolectric-gradle-plugin:1.1.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,31 +158,18 @@ public UserIdentity getUserIdentity () {

@Override
public DeviceIdentity getDeviceIdentity () {
//Not getting rid of this function because, this class implements an interface from another SDK and the interface expects this method.
//returning null here because we no longer have oauth_client in our tokens.
logger.debug("getDeviceIdentity");
IdentityToken identityToken = getIdentityToken();
if (identityToken == null) {
return null;
}
Map map = new HashMap();
map.put(DeviceIdentity.ID, identityToken.getOAuthClient().getDeviceId());
map.put(DeviceIdentity.OS, identityToken.getOAuthClient().getDeviceOS());
map.put(DeviceIdentity.MODEL, identityToken.getOAuthClient().getDeviceModel());
map.put(DeviceIdentity.BRAND, Build.BRAND);
map.put(DeviceIdentity.OS_VERSION, Build.VERSION.RELEASE);
return new BaseDeviceIdentity(map);
return null;
}

@Override
public AppIdentity getAppIdentity () {
//Not getting rid of this function because, this class implements an interface from another SDK and the interface expects this method.
//returning null here because we no longer have oauth_client in our tokens.
logger.debug("getAppIdentity");
IdentityToken identityToken = getIdentityToken();
if (identityToken == null) {
return null;
}
Map map = new HashMap();
map.put(AppIdentity.ID, identityToken.getOAuthClient().getSoftwareId());
map.put(AppIdentity.VERSION, identityToken.getOAuthClient().getSoftwareVersion());
return new BaseAppIdentity(map);
return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
public interface IdentityToken extends Token {
String getName();
String getEmail();
String getGender();
String getLocale();
String getPicture();
JSONArray getIdentities();
OAuthClient getOAuthClient();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,15 @@ public class Config {
public final static String REGION_GERMANY_OLD = ".eu-de.bluemix.net";
public final static String REGION_TOKYO_OLD = ".jp-tok.bluemix.net";

private final static String OAUTH_ENDPOINT = "/oauth/v3/";
private final static String OAUTH_ENDPOINT = "/oauth/v4/";
private final static String ATTRIBUTES_ENDPOINT = "/api/v1/";
private static final String serverUrlPrefix = "https://appid-oauth";
private static final String userProfilesPrefix = "https://appid-profiles";
private static final String PUBLIC_KEYS_ENDPOINT = "/publickeys";
private static final String PROTOCOL = "http";

private Config(){}

public static String getOAuthServerUrl (AppID appId) {
String region = appId.getBluemixRegion();
String serverUrl = convertEndpoints(appId.getBluemixRegion());

String serverUrl = (region != null && region.startsWith(PROTOCOL)) ? region : serverUrlPrefix + region;
serverUrl += OAUTH_ENDPOINT;

if (null != appId.overrideOAuthServerHost) {
Expand All @@ -46,8 +42,7 @@ public static String getOAuthServerUrl (AppID appId) {
}

public static String getUserProfilesServerUrl (AppID appId) {
String region = appId.getBluemixRegion();
String serverUrl = (region != null && region.startsWith(PROTOCOL)) ? region : userProfilesPrefix + region;
String serverUrl = convertEndpoints(appId.getBluemixRegion());

if (null != appId.overrideUserProfilesHost) {
serverUrl = appId.overrideUserProfilesHost;
Expand All @@ -63,41 +58,42 @@ public static String getPublicKeysEndpoint (AppID appId) {
public static String getIssuer(AppID appId) {

if (null != appId.overrideOAuthServerHost) {
return appId.overrideOAuthServerHost.split("/")[2];
String[] overrideServerUrlSplit = appId.overrideOAuthServerHost.split("/");
return overrideServerUrlSplit[0] + "//" + overrideServerUrlSplit[2] + OAUTH_ENDPOINT + appId.getTenantId();
}

String region = appId.getBluemixRegion();
if (region == null) {
return serverUrlPrefix;
}

String issuer = region.contains("cloud.ibm.com") ? serverUrlPrefix + suffixFromRegion(region) :
Config.getOAuthServerUrl(appId);

return issuer.split("/")[2];
return Config.getOAuthServerUrl(appId);
}

private static String suffixFromRegion(String region) {
switch (region) {
case AppID.REGION_UK_STAGE1:
return ".stage1" + REGION_UK_OLD;
case AppID.REGION_US_SOUTH_STAGE1:
return ".stage1" + REGION_US_SOUTH_OLD;
case AppID.REGION_US_SOUTH:
return REGION_US_SOUTH_OLD;
case AppID.REGION_UK:
return REGION_UK_OLD;
case AppID.REGION_SYDNEY:
return REGION_SYDNEY_OLD;
case AppID.REGION_GERMANY:
return REGION_GERMANY_OLD;
case AppID.REGION_US_EAST:
return REGION_US_EAST_OLD;
case AppID.REGION_TOKYO:
return REGION_TOKYO_OLD;

/**
* converts old bluemix.net endpoints to new cloud.ibm.com endpoints
* @param region
* @return
*/
private static String convertEndpoints(String region) {

if(region != null && region.contains("bluemix.net")) {
switch (region) {
case ".stage1" + REGION_UK_OLD:
return AppID.REGION_UK_STAGE1;
case ".stage1" + REGION_US_SOUTH_OLD:
return AppID.REGION_US_SOUTH_STAGE1;
case REGION_US_SOUTH_OLD:
return AppID.REGION_US_SOUTH;
case REGION_UK_OLD:
return AppID.REGION_UK;
case REGION_SYDNEY_OLD:
return AppID.REGION_SYDNEY;
case REGION_GERMANY_OLD:
return AppID.REGION_GERMANY;
case REGION_US_EAST_OLD:
return AppID.REGION_US_EAST;
case REGION_TOKYO_OLD:
return AppID.REGION_TOKYO;
}
}

return region;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.IncorrectClaimException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
Expand Down Expand Up @@ -346,14 +348,27 @@ protected Key getPublickey(Response response, String tokenKid) throws Authorizat
}
}

protected boolean verifyToken(Key rsaPublicKey, String token, String issuer, String audience, String tenant) throws SignatureException,IncorrectClaimException {
protected boolean verifyToken(Key rsaPublicKey, String token, String issuer, String clientId, String tenant) throws SignatureException,IncorrectClaimException {
if (rsaPublicKey == null){
return false;
}
try {
Jwts.parser().requireIssuer(issuer).requireAudience(audience)

Claims claims = Jwts.parser().requireIssuer(issuer)
.require("tenant", tenant).setSigningKey(rsaPublicKey)
.parseClaimsJws(token).getBody();

try {
//since the jwt library does not support audience as an array yet, we do the validation manually.
ArrayList<String> aud = claims.get("aud", ArrayList.class);

if(aud == null || !aud.contains(clientId)) {
throw new IncorrectClaimException(null, claims, "Invalid audience");
}
} catch (ClassCastException ce) {
throw new IncorrectClaimException(null, claims, "Invalid audience");
}

return true;
} catch (SignatureException|IncorrectClaimException exception) { // Invalid signature/claims
throw exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public abstract class AbstractToken implements Token {
private final static String ISSUED_AT = "iat";
private final static String TENANT = "tenant";
private final static String AUTHENTICATION_METHODS = "amr";
private final static String VERSION = "ver";


private final static Logger logger = Logger.getLogger(Logger.INTERNAL_PREFIX + AbstractToken.class.getName());
Expand Down Expand Up @@ -96,8 +97,8 @@ public String getSubject () {
}

@Override
public String getAudience () {
return (String) getValue(AUDIENCE);
public List<String> getAudience () {
return convertJsonArrayToList(AUDIENCE);
}

@Override
Expand All @@ -119,15 +120,7 @@ public String getTenant () {

@Override
public List<String> getAuthenticationMethods(){
List<String> list = new ArrayList<>();
JSONArray array = (JSONArray) getValue(AUTHENTICATION_METHODS);
for (int i=0; i<array.length(); i++){
try {
list.add(array.getString(i));
} catch (JSONException e){}
}

return list;
return convertJsonArrayToList(AUTHENTICATION_METHODS);
}

protected Object getValue (String name){
Expand All @@ -139,11 +132,34 @@ protected Object getValue (String name){
}
}

protected List<String> convertJsonArrayToList(String name) {
List<String> list = new ArrayList<>();
JSONArray array = (JSONArray) getValue(name);
for (int i=0; i<array.length(); i++){
try {
list.add(array.getString(i));
} catch (JSONException e){}
}

return list;
}

public boolean isExpired(){
return getExpiration().before(new Date());
}

public boolean isAnonymous() {
return getAuthenticationMethods().contains(IDP_ANONYMOUS);
}

@Override
public Integer getVersion() {

try {
return (int) getHeader().get(VERSION);
} catch (JSONException e) {
logger.error("Failed to retrieve " + VERSION, e);
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package com.ibm.cloud.appid.android.internal.tokens;

import com.ibm.cloud.appid.android.api.tokens.IdentityToken;
import com.ibm.cloud.appid.android.api.tokens.OAuthClient;
import com.ibm.mobilefirstplatform.clientsdk.android.logger.api.Logger;

import org.json.JSONArray;
Expand All @@ -25,7 +24,6 @@ public class IdentityTokenImpl extends AbstractToken implements IdentityToken {
private static final Logger logger = Logger.getLogger(Logger.INTERNAL_PREFIX + IdentityTokenImpl.class.getName());
private final static String NAME = "name";
private final static String EMAIL = "email";
private final static String GENDER = "gender";
private final static String LOCALE = "locale";
private final static String PICTURE = "picture";
private final static String IDENTITIES = "identities";
Expand All @@ -44,11 +42,6 @@ public String getEmail () {
return (String) getValue(EMAIL);
}

@Override
public String getGender () {
return (String) getValue(GENDER);
}

@Override
public String getLocale () {
return (String) getValue(LOCALE);
Expand All @@ -68,9 +61,4 @@ public JSONArray getIdentities () {
return new JSONArray();
}
}

@Override
public OAuthClient getOAuthClient () {
return new OAuthClientImpl(this);
}
}
Loading

0 comments on commit 079a75e

Please sign in to comment.