MSAL B2C 密码重置流程导致 "invalid grant" 错误

MSAL B2C PasswordReset Flow causes "invalid grant" error

我正在尝试使用 iOS 上的 MSAL(1.1.7) 库构建登录/注册和密码重置流程。

对于 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 
    }
}

错误似乎很清楚,如果我的理解至少是正确的,则没有用户,只有链接到 B2C_1A_PasswordReset 策略的 accessToken 和 refreshToken。并且需要手动用户登录。

我现在应该为用户启动手动登录流程吗?

iOS 忘记密码流程将变得非常愚蠢:

  1. 点按帐户(根据 B2C_1A_SignUpSignIn 政策要求令牌交互)
  2. iOS提示允许在safari中打开webview
  3. 用户在网络视图中点击“忘记密码”
  4. 重定向回捕获错误代码的应用程序 (AADB2C90118)
  5. 使用 B2C_1A_PasswordReset
  6. 的策略以交互方式启动令牌
  7. iOS提示允许在safari中打开webview
  8. 用户跳过所有重设密码圈
  9. 重定向回带有 accessToken 和 refreshToken 的应用程序(我不能将其用于任何用途)
  10. 使用 B2C_1A_SignUpSignIn
  11. 的策略以交互方式启动令牌
  12. iOS提示允许在safari中打开webview
  13. 用户使用新凭据登录
  14. 重定向回带有 accessToken 和 refreshToken 的应用(我 可以 使用)

Android 不强制提示打开浏览器,对于我开发该流程的同事,用户在重置密码后登录,似乎是正在后台成功登录。

我无法在 iOS 的文档中找到密码重置流程的示例。有其他平台的例子。

我可以用不同的方式构建它以获得更好的流程吗?

我们可以访问后端,我们可以在那里做些什么吗?

策略和在其上下文中检索到的令牌是相关联的。不同的策略可能有不同的权限和流程,因此从 MSAL/apps 的角度来看,它们也可能是不同的后端。

好的,更好的流程是确保在使用 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)

具有执行特定任务所需的权限。因此,就我而言,当用户需要重置密码时,我会更改权限,转到 webview 并重置密码,取回令牌,它们会自动放入 MSAL。现在,当我调用 getTokenSilently 时,我会使用 SignInSignUp 策略,但 MSAL 中的令牌将链接到密码重置策略 =>“无效授予”

我做了什么来修复它,因为我仍然认为仅在需要时实例化 MSALPublicClientApplication 的模式要好得多,因为这与各种 API 调用不同的地方有关该应用程序,而不只是在一个 ViewController 中,是在 MSALPublicClientApplication 上使用一个应用程序 class 方法进行扩展,该方法从上次成功的 getTokenSilently 或 [=19] 中获取策略=] 并始终使用它来构造应用程序对象。

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 策略刷新它 =>“无效授权”。