Implementing an OAuth2 login flow in WKWebView

Nicholas Solter
Posts from Emmerge
Published in
3 min readOct 29, 2015

Introduced in iOS 8, WKWebView is the much-improved (over UIWebView) view for rendering “interactive web content” in a native iOS app. One scenario where a web view comes in handy is a client-side OAuth2 login flow.

What is OAuth2?

Most web services like Google, Facebook, Twitter, and many others provide a login flow for users to authenticate with the service and authorize your app to access their services. This login flow is generally some variant of OAuth2. Although the full OAuth2 protocol is complex, the client-side (web-based) logins generally consist of a simple process in which your app directs the user to an authorization page provided by the third-party service, which handles your user’s login (validates her username and password, for example), and then redirects the browser back to your app with an authorization code or token.

In a web app, this is generally straightforward. In iOS it can be slightly harder. Some services (like Google and Facebook) provide iOS SDKs that wrap the login flow, so you don’t have to implement it yourself. However, not all services provide an SDK for iOS. I faced this problem recently when trying to integrate Nylas authentication into the emmerge iOS app I’m building.

Enter the Web View

WKWebView basically lets you provide a mini web browser in your iOS app. You can use it to kick off the OAuth2 login with a third-party service such as Nylas, and then wait for the redirect call.

Wait, What’s That About a Redirect?

The redirect is the last part of the OAuth2 flow. When you start the login flow you direct the user to a URI that looks something like

https://usefulservice.com/oauth/authorize?client_id=xyz&response_type=code&redirect_uri=…

You specify a redirect_uri parameter, to which the browser redirects with an access token or code (which can be exchanged for a token) after the user has authorized the service. Usually this redirect URI sends the browser back to your own app’s server, which can parse out the access token or code.

Handling the Redirect URI in iOS

In a native app, you generally need to handle the redirect URI in the app itself. The standard way to handle a redirect URI or deep link in iOS is to use a custom scheme (“yourappname://auth-response” for example) and implement the following method of the UIApplicationDelegate

 — (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation

If the url matches your custom scheme, parse out the access token. Otherwise, return NO.

if (![[[url scheme] lowercaseString] isEqualToString: [self appURLScheme]]) {
return NO;
}
// parse the access token out, etc...

But There’s a Problem

Unfortunately, WKWebView doesn’t send urls with custom schemes back to your app to handle automatically. If you try this without special handling, it will look like your web view hangs after the user authenticates with the third-party service and you’ll never receive your callback. You could try using a redirect URI with the standard http or https scheme, but then WKWebView would just try to load it, rather than directing it out of the web view to your native app to handle. So how do you get the WKWebView to recognize this “special” URL and handle it accordingly?

Use the Delegate

In order to handle the redirect, you need to implement decidePolicyForNavigationAction in the WebPolicyDelegate of your WKWebView to detect the custom URL scheme and direct it to your app to be handled:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {    UIApplication *app = [UIApplication sharedApplication];
NSURL *url = navigationAction.request.URL;
NSString *myScheme = @"...";
if ([url.scheme isEqualToString:myScheme] &&
[app canOpenURL:url]) {
[app openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}

Don’t forget to set your web view’s policy delegate to the appropriate object, of course.

Spicing up the Web View

Using a basic WKWebView will get the job done. But in order to give your user a nice login experience, you probably want to embed your web view in a UINavigationController, and provide some loading indicators, a “cancel” button, and so on. There are several libraries, such as STKWebKitViewController, that can do this for you.

As always, I’m curious to hear other’s experiences with this issue!

--

--