Issue #1012
Web-based authentication flows require your app to hand control to Safari, let the user authenticate, and then return to your app with the results. ASWebAuthenticationSession manages this handoff, but you need to tell it how to recognize when authentication completes. The framework gives you two approaches: custom URL schemes and universal links.
The Two Callback Methods
ASWebAuthenticationSession identifies completed authentication by watching for specific URLs. When the web page redirects to a URL that matches your callback pattern, the session captures it and returns control to your app.
Custom URL schemes offer the simplest setup. Register a scheme like myapp:// in your Info.plist, and the system routes those URLs directly to your application. When the OAuth provider redirects to myapp://callback?token=abc123, ASWebAuthenticationSession intercepts it and passes the full URL to your completion handler.
Universal links provide a more seamless experience. Instead of a custom scheme, you use a regular HTTPS URL that you own, like https://myapp.com/auth/callback. This requires configuring your web server with an apple-app-site-association file, but users see a standard web URL rather than a custom scheme. The approach looks more professional and avoids the jarring “Open in App?” dialogs that sometimes appear with custom schemes.
Using Custom URL Schemes
Start by registering your custom scheme in Info.plist. Add a URL type with an identifier and your chosen scheme. Keep the scheme unique enough to avoid conflicts with other apps.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.myapp</string>
</dict>
</array>
Configure your OAuth provider to redirect to your custom scheme after authentication. Most providers let you specify a redirect URI in their dashboard. Use something like myapp://callback or myapp://auth/complete.
Create the authentication session with your OAuth URL and callback scheme. The session presents Safari, waits for the redirect, and delivers the callback URL.
import AuthenticationServices
func startAuthentication() {
let authURL = URL(string: "https://provider.com/oauth/authorize?client_id=YOUR_ID&redirect_uri=myapp://callback")!
let callbackScheme = "myapp"
let session = ASWebAuthenticationSession(
url: authURL,
callbackURLScheme: callbackScheme
) { callbackURL, error in
if let error = error {
// Handle cancellation or other errors
print("Authentication failed: \(error)")
return
}
guard let callbackURL = callbackURL else {
print("No callback URL received")
return
}
// Extract the token from the URL
self.handleCallback(url: callbackURL)
}
session.presentationContextProvider = self
session.start()
}
func handleCallback(url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let token = components.queryItems?.first(where: { $0.name == "token" })?.value else {
return
}
// Use the token
print("Received token: \(token)")
}
The completion handler runs on the main thread, so you can update UI directly. Check for errors first, since users can cancel the authentication flow by tapping Done. Extract any tokens or authorization codes from the callback URL’s query parameters.
Using Universal Links
Universal links require more setup but provide a better user experience. Start by hosting an apple-app-site-association file on your domain at https://myapp.com/.well-known/apple-app-site-association. The file tells iOS which paths your app handles.
{
"applinks": {
"apps": [],
"details": [{
"appID": "TEAMID.com.example.myapp",
"paths": ["/auth/callback"]
}]
}
}
Add the associated domain entitlement to your app. In Xcode, go to your target’s Signing & Capabilities, add Associated Domains, and include your domain with the applinks prefix.
applinks:myapp.com
Configure your OAuth provider to redirect to your HTTPS callback URL. Use something like https://myapp.com/auth/callback instead of a custom scheme.
The authentication session setup looks nearly identical to the custom scheme approach. The key difference is the callback scheme parameter. Pass https as the scheme, and ASWebAuthenticationSession will match universal links.
func startAuthentication() {
let authURL = URL(string: "https://provider.com/oauth/authorize?client_id=YOUR_ID&redirect_uri=https://myapp.com/auth/callback")!
let callbackScheme = "https"
let session = ASWebAuthenticationSession(
url: authURL,
callbackURLScheme: callbackScheme
) { callbackURL, error in
if let error = error {
print("Authentication failed: \(error)")
return
}
guard let callbackURL = callbackURL else {
print("No callback URL received")
return
}
self.handleCallback(url: callbackURL)
}
session.prefersEphemeralWebBrowserSession = false
session.presentationContextProvider = self
session.start()
}
The callback handling works the same way. Parse the URL components and extract your authentication data.
Universal links feel more professional and integrate better with iOS. Users see regular web URLs, and the transition back to your app feels seamless. The setup cost is higher, but the improved user experience usually justifies the effort for production apps.
Start the conversation