iosswiftfirebase-authenticationokta

Firebase signIn(with: AuthCredential) signs in using wrong account


I am using the Firebase signIn(with: AuthCredential) method on iOS to sign in with OKTA like so and I found this strange behaviour:

var provider = OAuthProvider(providerID: providerId, auth: auth)
provider.customParameters = ["login_hint": email]
provider.scopes = ["email", "profile", "address"]
let credential = try await provider.credential(with: nil)
let result = try await auth.signIn(with: credential)

Steps to reproduce:

  1. Sign in the app with some account A
  2. Sign out of the app.
  3. Try to sign in with some account B (with different email)
  4. You are now signed in with account A

What happens is, that signIn(with:) redirects to an embedded browser where the sign in is done, but the second time the credentials are somehow cached in the browser (I guess), so after redirecting to the browser, the login page is skipped and user is signed in with the previous credentials.

It feels pretty unsafe, I mean sure you had to be signed in as account A on this device already, but still.

How can I stop this behaviour? I tried to delete all sorts of cache before launching the app, but nothing is helping.

Here is what I have:

// Cookies
let cookieStorage = HTTPCookieStorage.shared
for cookie in cookieStorage.cookies ?? [] {
    cookieStorage.deleteCookie(cookie)
}
// Keychain
let secItemClasses = [kSecClassGenericPassword,
    kSecClassInternetPassword,
    kSecClassCertificate,
    kSecClassKey,
    kSecClassIdentity]
for secItemClass in secItemClasses {
    let dictionary = [kSecClass as String:secItemClass]
    SecItemDelete(dictionary as CFDictionary)
}
// Credentials
let credentialStorage = URLCredentialStorage.shared
for entry in credentialStorage.allCredentials {
    let space = entry.key
    for (user, credential) in entry.value {
        credentialStorage.remove(credential, for: space)
    }
}
// WebsiteData
let dataStore = WKWebsiteDataStore.default()
let records = await dataStore
    .dataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes())
for record in records {
    await dataStore.removeData(ofTypes: record.dataTypes, for: [record])
}
// Firestore
try await Firestore.firestore().clearPersistence()

Solution

  • It is common for openid authentication providers to use cookies and sign a user in without requiring authentication if they already have a session.

    Generally this isn't a concern as devices are not often shared and they are typically protected by some form of local authentication such as a passcode or biometrics.

    You can force Okta to require authentication by adding prompt=login to the customParameters. You will also need to add openid to the scopes to indicate that this is an openid authentication request.