Авторизация средствами AuthorizationService

171
20 июня 2018, 05:00

коллеги! Опишу кратко ситуацию (приготовтесь к тяжелому материалу): Есть приложение с авторизацией через сайт, средствами вот этой библиотеки AppAuth (так хочет заказчик). Так же он хочет два приложения в одном проекте (разделение выполнено через productFlavors). Проблема в том, что данные об авторизации сохраняются где-то в куках (или еще где - этого я как раз понять не могу). Если я авторизируюсь в П1, и открою П2, то после отправки запроса на сайт об авторизации, мне не будет показан экран с вводом пароля и логина (показывается он средствами бразуера смартфона). Экран пропущен, я буду считаться как уже авторизированные (ведь так оно и есть, но в П1, а не в П2 и в этом будет ошибка). Как заставить браузер забыть о том, что я авторизирован при захочет с другого приложения? (очистить куки, или еще какую магию знает кто)

Ниже я приведу участки кода:

Вызов экрана авторизации:

public void startLogin() {
    AuthorizationServiceConfiguration serviceConfig =
            new AuthorizationServiceConfiguration(
                    Uri.parse(AUTH_ENDPOINT), // authorization endpoint
                    Uri.parse(TOKEN_ENDPOINT)); // token endpoint
    AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder(
            serviceConfig,
            CLIENT_ID,
            ResponseTypeValues.CODE,
            Uri.parse(getString(R.string.app_login_callback_uri))
    ).setScope(APIConfig.SCOPE);
    AuthorizationRequest request = builder.build();
    Intent authIntent = authorizationService.getAuthorizationRequestIntent(request);
    startActivityForResult(authIntent, AUTH_REQUEST_KEY);
}

Перехват результата от браузера:

 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode != AUTH_REQUEST_KEY || resultCode != RESULT_OK) {
        //LOGOUT_REQUEST_KEY is called when leaving logout webview
        //if logout webview is cancelled or back button is closed, result value is 0
        resetViewState();
        return;
    }
    AuthorizationResponse response = AuthorizationResponse.fromIntent(data);
    AuthorizationException error = AuthorizationException.fromIntent(data);
    if (response == null) {
        handleLoginError(getString(R.string.login_screen_error_login_cancelled));
        return;
    }
    final AuthState authState = new AuthState(response, error);
    authorizationService.performTokenRequest(response.createTokenExchangeRequest(), (tokenResponse, exception) -> {
        if (exception != null) {
            Log.e(TAG, "Token Exchange failed", exception);
            exception.printStackTrace();
            handleLoginError(getString(R.string.login_screen_error_token_request_failed));
            return;
            //handleLoginError(exception.getMessage());
        }
        if (tokenResponse != null) {
            authState.update(tokenResponse, null);
            if (BuildConfig.FLAVOR_role.equalsIgnoreCase("manager")) {
                if (authState.getAccessToken() != null) {
                    JWT jwt = new JWT(authState.getAccessToken());
                    Claim manager = jwt.getClaim("role");
                    List<String> roles = manager.asList(String.class);
                    boolean isManager = roles.contains("manager") || roles.contains("\"manager\"");
                    if (!isManager) {
                        Toast.makeText(getApplicationContext(), "Access denied", Toast.LENGTH_SHORT).show();
                        binding.setIsLoading(false);
                        return;
                    }
                }
            }
            AuthStateHelper.saveAuthState(
                    getBaseContext(),
                    authState.getAccessToken(),
                    authState.getIdToken(),
                    authState.getRefreshToken());
            showHomepage();
            return;
            /*
            Log.i(TAG,"Auth state: "+authStateString);
            String authStateString = authState.jsonSerializeString();
            Log.i(TAG,
                    String.format("Token Response [ Access Token: %s, ID Token: %s , Refresh Token: %s]",
                            tokenResponse.accessToken, tokenResponse.idToken, tokenResponse.refreshToken));*/
        }
        handleLoginError(getString(R.string.login_screen_error_token_request_failed));
    });
}

Сохранение токенов (и прочей прихоядщей информации о авторизации) я сохраняю в shared Preference. там я тоже реализовал раздельное хранение, но неожиданно это не помогло. В связи с этим, я предположил, что сохранение данных происходит в самом бразуре или сервере (но скорее всего браузере). Если приложение удалить, и установить заново - то придется вводить пользовательские данные. Если разлогиниться, то выкенет на экран авторизации в обоих приложениях.

ПРАВКА: applicationId Для обоих приложений разный:

flavorDimensions 'env', 'role'
productFlavors {
    daily {
        dimension 'role'
        applicationId = "com.xxx.aaa"
        proguardFile 'proguard-rules.pro'
    }
    manager {
        dimension 'role'
        applicationId = "com.xxx.aaa.manager"
        proguardFile 'proguard-rules-manager.pro'
    }
    dev {
        dimension 'env'
        applicationIdSuffix ".dev"
        manifestPlaceholders = [
                'appAuthRedirectScheme': 'com.xxx.aaa.dev'
        ]
    }
    live {
        dimension 'env'
        manifestPlaceholders = [
                'appAuthRedirectScheme': 'com.xxx.aaa'
        ]
    }
}

А так же привожу AuthStateHelper:

public class AuthStateHelper {
public static void saveAuthState(Context context,
                                 String accessToken,
                                 String idToken,
                                 String refreshToken) {
    String IVString = null;
    String encryptedRefreshToken = null;
    String newAuthStateString = null;
    EncryptionHandler handler = new EncryptionHandler(context);
    byte[] IV = handler.generateIV();
    IVString = Base64.encodeToString(IV, Base64.NO_WRAP);
    encryptedRefreshToken = new EncryptionHandler(context).encryptValue(IV, refreshToken);
    //newAuthStateString = AuthStateHelper.removeRefreshToken(authState.jsonSerializeString());
    //SharedPrefsUtils.saveAuthState(context, newAuthStateString);
    SharedPrefsUtils.saveIV(context, IVString);
    SharedPrefsUtils.saveRefreshToken(context, encryptedRefreshToken);
    SharedPrefsUtils.saveIdToken(context, idToken);
    SharedPrefsUtils.saveAccessToken(context, accessToken);
}
/**
 * Load saved auth state and add refresh token to auth state after decrypting it
 *
 * @param context Context
 * @return Auth state
 */
public static String getRefreshToken(Context context) {
    String IV = SharedPrefsUtils.getIV(context);
    String encryptedRefreshToken = SharedPrefsUtils.getRefreshToken(context);
    EncryptionHandler handler = new EncryptionHandler(context);
    return handler.decryptValue(encryptedRefreshToken, IV);
}
}

ПРАВКА № 2 Продебажил авторизацию, с целью изучения токенов. Использовал https://jwt.io/ . Получилось, что ответ возвращаемый сервером содержит одни поля. Ниже привожу accessToken cо слегка измененными. Для обоих приложений токены одинаковые, за исключение двух первых полей. Я увидел там несколько видов id, но как их настриоть?)

Как я уже говорил, SharedPrefsUtils разделены для обоих приложений. Надеюсь, вы сможете еще что-то мне посоветовать

Answer 1

Без наличия AuthStateHelper предположу, что оба приложения подписаны одним ключом, поэтому читают одни и те же настройки - старый токен из другого приложения.

Выложите helper, build.gradle (меняете или нет applicationId и т.д.)

READ ALSO
Смешиваются потоки

Смешиваются потоки

Подскажите, где может быть проблема: Имею сервер, к которому соединяются устройства по tcp socket, на каждое красиво создаётся потокСоединившись,...

192
Выдаёт ошибку при компилиции, пока не дошел до switch

Выдаёт ошибку при компилиции, пока не дошел до switch

Выдаёт ошибку при компилиции, пока не дошел до switch всё идёт нормально, а потом ошибкаГде моя ошибка?

165
ConcurrentModificationException и Хешмап

ConcurrentModificationException и Хешмап

Здравствуйте кидает Exception ConcurrentModificationException на этой строчке for (Transaction t : cgetTransactions())(64 строчка) на 2 итерации

225
Импорт библиотеки JAXB в проект JavaFX

Импорт библиотеки JAXB в проект JavaFX

Есть проект на JavaFX, для хранения данных нужно использовать сериализацию в JAXBДля этого нужно использовать XML

171