Ошибка неверного состояния AppAuth oidc при аутентификации на сервере идентификации

Я настроил сервер идентификации 3 в качестве IdP для проекта, у нас есть 3 клиента: MVC web, IOS и Android. все хорошо для приложения MVC, использующего гибридный поток.

для IOS и Android с помощью собственного клиента oidc (AppAuth IOS и AppAuth android) не работает, хотя я настроил поток как гибридный с PKCE.

теперь, когда я пытаюсь создать POC на Android с помощью Xamarin и с помощью IdentityModel.oidcClient, все работает, как и ожидалось, получая токены доступа, обновления и идентификатора. при использовании AppAuth для IOS и Android я получаю следующую ошибку:

{"type":0,"code":9,"errorDescription":"Response state param did not match request state"}

есть идеи, чего не хватает?

Я подозреваю, что эти два собственных клиента oidc не запрашивают общий секрет клиентов, поэтому из-за этого поток поврежден.


person Ali Bazzi    schedule 01.02.2017    source источник
comment
это связано с тем, что ответ авторизации возвращается как фрагмент в uri перенаправления, в то время как библиотеки oidc ожидают строку запроса в качестве ответа, при этом библиотекам не удается получить ответ. библиотеки не соответствуют спецификациям, на самом деле я сомневаюсь, что обе библиотеки все равно поддерживают гибридный поток. они обрабатывают случай Google IdP, и это все для разработчика.   -  person Ali Bazzi    schedule 02.02.2017
comment
Библиотеки не относятся к Google; они реализуют рекомендации OAuth2 для нативных приложений: tools.ietf. org/html/draft-ietf-oauth-native-apps-07   -  person iainmcgin    schedule 13.02.2017


Ответы (2)


Данные должны совпадать в мобильном приложении и на Identity Server,

На сервере:

new Client
        {
            ClientId = "myClientId",
            ClientName = "myClientName",
            AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
            RequireConsent = false,

            ClientSecrets =
            {
                new Secret("myClientSecret".Sha256())
            },

            RedirectUris = { "myRedirectUri://callback" },

            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.Email,
                IdentityServerConstants.StandardScopes.Phone,
            },

            AllowOfflineAccess = true
        }

При входе в систему в Android:

    AuthManager authManager = AuthManager.getInstance(this);
    AuthorizationService authService = authManager.getAuthService();
    Auth auth = authManager.getAuth();

    AuthorizationRequest authRequest = new AuthorizationRequest
        .Builder(
        authManager.getAuthConfig(),
        auth.getClientId(),
        auth.getResponseType(),
        Uri.parse(auth.getRedirectUri()))
        .setScope(auth.getScope())
        .build();

    Intent authIntent = new Intent(this, LoginAuthActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, authRequest.hashCode(), authIntent, 0);

    authService.performAuthorizationRequest(
        authRequest,
        pendingIntent);

Спросите токен:

    final AuthorizationResponse resp = AuthorizationResponse.fromIntent(getIntent());
    AuthorizationException ex = AuthorizationException.fromIntent(getIntent());

    final AuthManager authManager = AuthManager.getInstance(this);
    authManager.setAuthState(resp,ex);

    if (resp != null) {

        ClientSecretPost clientSecretPost = new ClientSecretPost(authManager.getAuth().getClientSecret());
        TokenRequest tokenRequest = new TokenRequest
            .Builder(authManager.getAuthConfig(), authManager.getAuth().getClientId())
            .setAuthorizationCode(resp.authorizationCode)
            .setRedirectUri(Uri.parse(authManager.getAuth().getRedirectUri()))
            .build();

        mAuthService = authManager.getAuthService();

        mAuthService.performTokenRequest(tokenRequest, clientSecretPost, new AuthorizationService.TokenResponseCallback() {
            @Override public void onTokenRequestCompleted(@Nullable TokenResponse response, @Nullable AuthorizationException ex) {
                if(ex == null) {
                    authManager.updateAuthState(response,ex);
                    MyApp.Token = authManager.getAuthState().getIdToken();
                    startService(new Intent(LoginAuthActivity.this, TokenService.class));
                    Intent mainIntent = new Intent(LoginAuthActivity.this, MainActivity.class);
                    startActivity(mainIntent);
                    finish();
                }
                else{
                    Intent loginIntent = new Intent(LoginAuthActivity.this, LoginActivity.class);
                    startActivity(loginIntent);
                    finish();
                }
            }
        });

        // authorization completed
    } else {
        // authorization failed, check ex for more details
        Intent loginIntent = new Intent(LoginAuthActivity.this, LoginActivity.class);
        startActivity(loginIntent);
        finish();
    }

Класс AuthManager:

public class AuthManager {
private static AuthManager instance;
private AuthState mAuthState;
private Auth mAuth;
private AuthorizationServiceConfiguration mAuthConfig;
private SharedPreferencesRepository mSharedPrefRep;
private AuthorizationService mAuthService;

public static AuthManager getInstance(Context context) {
    if (instance == null) {
        instance = new AuthManager(context);
    }
    return instance;
}

private AuthManager(Context context){
    mSharedPrefRep = new SharedPreferencesRepository(context);
    setAuthData();
    mAuthConfig = new AuthorizationServiceConfiguration(
            Uri.parse(mAuth.getAuthorizationEndpointUri()),
            Uri.parse(mAuth.getTokenEndpointUri()),
            null);
    mAuthState = mSharedPrefRep.getAuthState();

    mAuthService = new AuthorizationService(context);
}



public AuthorizationServiceConfiguration getAuthConfig() {
    return mAuthConfig;
}

public Auth getAuth() {
    if(mAuth == null){
       setAuthData();
    }

    return mAuth;
}

public AuthState getAuthState(){
    return mAuthState;
}

public void updateAuthState(TokenResponse response, AuthorizationException ex){
    mAuthState.update(response,ex);
    mSharedPrefRep.saveAuthState(mAuthState);
}

public void setAuthState(AuthorizationResponse response, AuthorizationException ex){
    if(mAuthState == null)
        mAuthState = new AuthState(response,ex);

    mSharedPrefRep.saveAuthState(mAuthState);
}

public AuthorizationService getAuthService(){
    return mAuthService;
}

private void setAuthData(){
    mAuth = new Auth();
    mAuth.setClientId(BuildConfig.CLIENT_ID);
    mAuth.setAuthorizationEndpointUri(BuildConfig.AUTHORIZSTION_END_POINT_URI);
    mAuth.setClientSecret(BuildConfig.CLIENT_SECRET);
    mAuth.setRedirectUri(BuildConfig.REDIRECT_URI);
    mAuth.setScope(BuildConfig.SCOPE);
    mAuth.setTokenEndpointUri(BuildConfig.TOKEN_END_POINT_URI);
    mAuth.setResponseType(BuildConfig.RESPONSE_TYPE);
}
}

Служба здесь запросит токен обновления.

Я сделал образец, используя Identity Server 4 с AppAuth-Android, вы можете проверить его здесь

person Hadi    schedule 08.07.2017
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. – Из обзора - person Alberto; 08.07.2017
comment
Я редактирую ответ - person Hadi; 09.07.2017

У нас есть открытый вопрос о поддержке гибридного потока на AppAuth-Android здесь. Основная проблема заключается в том, что гибридный поток плохо подходит для мобильных приложений, так как потребуется повторно запускать веб-поток через SafariViewController/CustomTab каждый раз, когда истечет срок действия токена доступа. Приобретение токена обновления для разрешения фонового обновления токенов доступа лучше для нативных приложений.

Поскольку IdentityServer3 является сертифицированной реализацией OpenID Connect, вы должны иметь возможность использовать поток кода авторизации для получения токена обновления.

person iainmcgin    schedule 13.02.2017