Skip to content

Commit

Permalink
Updates Server endpoints to v4 (#135)
Browse files Browse the repository at this point in the history
* Changes SDK to use server v4 endpoints. Updates test cases

* 1. Adds version and AZP validation 2. Updates iss validation
  • Loading branch information
icemann92 authored Feb 27, 2019
1 parent dde3e51 commit d028c5e
Show file tree
Hide file tree
Showing 22 changed files with 180 additions and 312 deletions.
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,34 @@ 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");
}

//verify azp
String azp = claims.get("azp", String.class);
if(azp == null || !azp.equals(clientId)) {
throw new IncorrectClaimException(null, claims, "Invalid azp");
}


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,8 @@ 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 = "version";
private final static String AZP = "azp";


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

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

@Override
Expand All @@ -119,15 +121,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 +133,39 @@ 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;
}
}

@Override
public String getAzp() {
return (String) getValue(AZP);
}
}
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 d028c5e

Please sign in to comment.