Поток сброса пароля MSAL B2C вызывает ошибку недопустимого предоставления

Я пытаюсь создать поток входа / регистрации и сброса пароля с помощью библиотеки MSAL (1.1.7) на iOS.

Для политики B2C_1A_SignUpSignIn вроде все работает нормально. Токены принимаются после обращения к браузеру и обратно.

Когда пользователь запрашивает сброс, нажав «Забыли пароль» в веб-просмотре: я поймаю код ошибки AADB2C90118 в соответствии с документами и запускаю новый поток с центром, у которого есть политика B2C_1A_PasswordReset.

let authority = try MSALB2CAuthority(url: authURL)
let config = MSALPublicClientApplicationConfig(clientId: AuthCredential.clientID.description,
                                            redirectUri: AuthCredential.redirectUri.description,
                                              authority: authority)
config.knownAuthorities = [authority]
            
let application = try MSALPublicClientApplication(configuration: config)
            
return application

Все в порядке, поток сброса, запускается, может быть завершен и приводит к accessToken и refreshToken.

Теперь возникает проблема. Эти недавно приобретенные токены действительны только для политики B2C_1A_PasswordReset, и когда я получаю токены незаметно для использования против нашего API, это происходит с политикой B2C_1A_SignUpSignIn. Это означает, что при первой попытке выполнить запрос, включающий вызов MSAL.getTokenSilently, я получаю следующую ошибку:

MSALErrorDescriptionKey=User interaction is required, 
MSALOAuthErrorKey=invalid_grant,
NSUnderlyingError=0x6000018a5590 {
    UserInfo = { MSALErrorDescriptionKey=AADB2C90088: The provided grant has not been issued for this endpoint. 
    Actual Value : B2C_1A_SignUpSignIn and Expected Value : B2C_1A_PasswordReset,
    MSALOAuthErrorKey=invalid_grant,
    MSALInternalErrorCodeKey=-42004 
    }
}

Ошибка кажется довольно очевидной, если я правильно понимаю, по крайней мере, нет пользователя, только accessToken и refreshToken, связанные с политикой B2C_1A_PasswordReset. И требуется ручной вход пользователя.

Могу ли я сейчас запустить процесс входа пользователя вручную?

В этом случае поток забытых паролей iOS был бы почти глупым:

  1. Коснитесь учетной записи (запросить токен в интерактивном режиме с политикой B2C_1A_SignUpSignIn)
  2. Подсказка iOS, позволяющая открыть веб-просмотр в сафари
  3. пользователи нажимают забытый пароль в веб-просмотре
  4. перенаправлен обратно в приложение, которое обнаруживает код ошибки (AADB2C90118)
  5. запустить токен в интерактивном режиме с политикой B2C_1A_PasswordReset
  6. Подсказка iOS, позволяющая открыть веб-просмотр в сафари
  7. Пользователь перескакивает через все обручи для сброса пароля
  8. перенаправлен обратно в приложение с помощью accessToken и refreshToken (которые я не могу использовать ни для чего)
  9. запустить токен в интерактивном режиме с политикой B2C_1A_SignUpSignIn
  10. Подсказка iOS, позволяющая открыть веб-просмотр в сафари
  11. пользователь входит в систему с новыми учетными данными
  12. перенаправлен обратно в приложение с помощью accessToken и refreshToken (которые я МОГУ использовать)

Android не выводит запрос на открытие браузера, и для моего коллеги, разрабатывающего этот поток, пользователь входит в систему после сброса пароля, и, похоже, вход в систему происходит успешно в фоновом режиме.

Мне не удалось найти пример процесса сброса пароля в документации для iOS. Есть примеры для других платформ.

Можно ли структурировать это иначе, чтобы улучшить поток?

У нас есть доступ к серверной части, что мы можем там сделать?


person RickiG    schedule 05.11.2020    source источник


Ответы (1)


Политики и токены, извлеченные в их контексте, связаны. Могут быть разные разрешения и потоки, связанные с разными политиками, поэтому с точки зрения MSAL / приложений они также могут быть разными бэкэндами.

Хорошо, лучше убедиться, что при использовании getTokenSilently вы также должны использовать токены, соответствующие политике, с которой был получен токен.

Во всех примерах MSAL, которые я нашел в Интернете, это не было проблемой, потому что они делают это непосредственно в ViewController и сохраняют ссылку на объект application. Вместо этого я каждый раз настраиваю его так:

let authority = try MSALB2CAuthority(url: authURL)
let config = MSALPublicClientApplicationConfig(clientId: AuthCredential.clientID.description,
                                            redirectUri: AuthCredential.redirectUri.description,
                                              authority: authority)
config.knownAuthorities = [authority]
            
let application = try MSALPublicClientApplication(configuration: config)

с авторитетом, который мне нужен для конкретной задачи. Итак, в моем случае, когда пользователю требовался сброс пароля, я бы сменил полномочия, перешел в веб-просмотр и сбросил пароль, получил обратно токены, они автоматически помещались в MSAL. Теперь, когда я звоню getTokenSilently, я бы использовал политику SignInSignUp, но токены в MSAL будут связаны с политикой сброса пароля = ›недействительный грант

То, что я сделал, чтобы исправить это, поскольку я все еще считаю, что шаблон создания экземпляра MSALPublicClientApplication только тогда, когда он вам нужен, намного лучше, поскольку он связан с различными вызовами API в разных местах приложения, а не только в одном ViewController, заключался в том, чтобы сделать расширение на MSALPublicClientApplication с помощью метода класса приложения, который берет политику из последнего успешного getTokenSilently или getTokenInteractively и всегда использует это для создания объекта приложения.

class func application() -> MSALPublicClientApplication? {
    do {
        let authURLString = UserDefaults.getMSALPolicyKey() ?? AuthCredential.signInAuthority.description
        let authURL = URL(string: authURLString)!
        let authority = try MSALB2CAuthority(url: authURL)
        
        let config = MSALPublicClientApplicationConfig(clientId: AuthCredential.clientID.description,
                                                       redirectUri: AuthCredential.redirectUri.description,
                                                       authority: authority)
        config.knownAuthorities = [authority]
        
        let application = try MSALPublicClientApplication(configuration: config)
        
        return application
        
    } catch {
        // catch and log error
        return nil
    }
}

и политика должна быть сохранена так:

application.acquireTokenSilent(with: silentParameters) { (result, error) in
    guard let result = result, let key = result.account.identifier, error == nil else {
    // Error handling
    UserDefaults.clearMSALPolicies() // clear policies so we default to standard policy 
        }
        
        completion(.failure(nsError))
    }
    // get policy directly from result
    UserDefaults.setMSALPolicyKey(key: result.authority.url.absoluteString)
    completion(.success(result.accessToken))
}

Это сокращает список потока сброса пароля в вопросе до 7 шагов, и пользователь входит в систему.

На Android это действительно не сработало, у нас просто были довольно долгоживущие токены, поэтому ошибка не появлялась там гораздо позже, когда срок действия токена с политикой сброса пароля истек, и клиент попытался обновить его с помощью политики SignInSignOut = › недействительный грант.

person RickiG    schedule 06.11.2020