# Debugging ASWebAuthenticationSession

In this blog post, you'll learn how to debug [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) and I'll share a trick on how to buy enough time to attach the Safari Web Inspector before logic executes.

You use `ASWebAuthenticationSession` to [authenticate a user through a web service](https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service). When the user navigates to the site’s authentication URL, the site presents the user with a form to collect credentials. After validating the credentials, the site redirects the user’s browser, typically using a custom scheme, to a URL that indicates the outcome of the authentication attempt.

```swift
// Use the URL and callback scheme specified by the authorization provider.
guard let authURL = URL(string: "https://example.com/auth") else { return }
let scheme = "exampleauth"

// Initialize the session.
let session = ASWebAuthenticationSession(url: authURL, callbackURLScheme: scheme)
{ callbackURL, error in
    // Handle the callback.
}
```

`ASWebAuthenticationSession` runs in a separate process and the content executed is HTML & JavaScript. Hence you can use the Safari Web Inspector to debug the HTML and JavaScript sources. The session is presented in a modal view controller that is similar in appearance to `SFSafariViewController,` but it is not Safari itself.

As a prerequisite, you have to [enable Safari Web Inspector](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/Web_Inspector_Tutorial/EnableWebInspector/EnableWebInspector.html#//apple_ref/doc/uid/TP40017576-CH3-DontLinkElementID_29) on your Mac

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678902535981/77bbd799-69a1-4de5-926c-4cd821c0b41b.png align="center")

as well as enabling it on your iOS device.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678902670247/96fb86c1-108e-4a83-ac4d-12de429471d6.png align="center")

Once `ASWebAuthenticationSession` presents the web content you can attach to the [Safari Web Inspector](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/Web_Inspector_Tutorial/DebuggingyourWebpage/DebuggingyourWebpage.html#//apple_ref/doc/uid/TP40017576-CH5-DontLinkElementID_24).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1679269861321/223304ba-e6e9-4a9c-97be-06796afd37a2.png align="center")

I often run into the tricky situation that the web content executes critical code before I can manually attach the Safari Web Inspector.

I came up with the following trick:

1. Replace the URL to be launched in `ASWebAuthenticationSession` with a different URL. The original URL gets encoded and passed as a URL query parameter.
    
2. Once the website is loaded I have all the time in the world to attach the Safari Web Inspector.
    
3. I manually trigger the navigation to the original URL through a button in the presented website of the `ASWebAuthenticationSession`.
    

All details are outlined below but frankly, there is an easier solution. Kudos to Markus Bösch for pointing out that you can a Web Proxy (e.g. Proxyman, Charles, or Fiddler) and set a breakpoint to pause, attach the Safari Web Inspector, and resume.

%[https://twitter.com/mboesch_ios/status/1638147053871067136] 

The approach from Markus is extremely nice as it does not require to re-compile the app!

Back to my original approach: I have deployed the following HTML/JS code as a [GitHub page](https://marcoeidinger.github.io/DelayedRelay/index.html) of a [GitHub repository](https://github.com/MarcoEidinger/DelayedRelay) of mine.

```xml
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <h1>Redirect</h1>
    <button onclick="redirect()">Redirect</button>
    <h2 id="demo"></h2>
    <script>
      function redirect() {
          const queryString = window.location.search;
          console.log(queryString);
          const urlParams = new URLSearchParams(queryString);
          const orig = urlParams.get('org')
          console.log(orig);
          var originalUrl = decodeURIComponent((orig).replace(/\+/g, '%20'));
          console.log(originalUrl);
          window.location.href = originalUrl;
      }
    </script>
  </body>
</html>
```

Replacing the URL is fairly easy if you use `ASWebAuthenticationSession` directly. But what if you use it indirectly, i.e. use a library that determines the original URL and passes it to `ASWebAuthenticationSession`?

You can use Method Swizzling to replace the initializer of `ASWebAuthenticationSession`. This works fine for a debug build but for some reason, this does not work for a release build.

```swift

import AuthenticationServices

extension ASWebAuthenticationSession {
    static func useDelayRelay() {
        ASWebAuthenticationSession.swizzleTheInit()
    }
}

extension ASWebAuthenticationSession {
    @objc static func swizzleTheInit() {
        let oldSelector = #selector(ASWebAuthenticationSession.init(url:callbackURLScheme:completionHandler:))
        let newSelector = #selector(ASWebAuthenticationSession.swizzledInit(url:callbackURLScheme:completionHandler:))
        let oldMethod = class_getInstanceMethod(self, oldSelector)!
        let newMethod = class_getInstanceMethod(self, newSelector)!
        let oldImplementation = method_getImplementation(oldMethod)
        let newImplementation = method_getImplementation(newMethod)
        _ = method_setImplementation(oldMethod, newImplementation)
        _ = method_setImplementation(newMethod, oldImplementation)
    }
    
    @objc private func swizzledInit(url: URL, callbackURLScheme: String?, completionHandler: @escaping ASWebAuthenticationSession.CompletionHandler) {
        let encoded = url.absoluteURL.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
        
        let queryItems: [URLQueryItem] = [.init(name: "org", value: encoded)]
        var urlComps = URLComponents(string: "https://marcoeidinger.github.io/DelayedRelay/index.html")!
        urlComps.queryItems = queryItems
        let new = urlComps.url!
        
        // calling the original implementation
        // note: does not work in release build configuration
        self.swizzledInit(url: new, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler)
    }
}
```

In this video I am illustrating all steps:

%[https://youtu.be/1TBDxKBGxVk]
