Secure and Usable OAuth for Mobile Apps
William Denniss, Product Manager, Google. @WilliamDenniss
We’ve spent a lot of energy promoting federated sign-on as a way to reduce password usage and improve the user experience.
But what happens when people don’t implement federated sign-on correctly?
The following is based on a true story.
Alibaba
*Phew*, I’m done.
48%
WebView
==
Bad conversion
WebView sign-in flows abandoned when no signed-in state.
Single Sign-On means you only sign-on once!
Wait a minute, can the host app inspect the webview?
The host app can extract the cookies:
String cookies = CookieManager.getInstance().getCookie(url);
Or inject javascript:
webView.evaluateJavascript(
"(function() { return document.getElementById('password').value; })();",
new ValueCallback<String>() {
@Override public void onReceiveValue(String s) {
Log.d("WebViewField", s);
}
});
Even trusted developers create risk
Third Party SDK
Third Party SDK
Third Party SDK
Trusted Developer
Enterprise
In-app Browser Tabs
To the rescue
In-App Browser Tabs
Secure context,
host app cannot inspect contents.
Shared cookie state
Custom tabs are
a system browser activity�presented in the app context
In-app Browser Tabs: A Better Way to Sign-in
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
If the user needs to login, they log in once
auth response
auth�request
Sign in
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Subsequent requests get SSO
auth response
auth�request
Sign in
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
More Complex “Chained Auth” Works Too
auth response
auth�request
Sign in
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Advanced Options: Mutual TLS with Certificates
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Advanced Options: FIDO U2F over NFC!
YubiKey NEO
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Most Devices support in-app browser tabs today!
96% of Android devices can run Chrome 45
84% of Apple devices support SFSafariViewController
Source: Apple, June 2016
Source: Google, June 2016
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
100% of users supported by launching �the browser as fallback
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Production Example
The Google Sign-in library on iOS uses in-app browser tabs today.
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Implementation Details
Draft IETF Best Current Practice
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Authorization Server Requirements
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
PKCE to Protect the Code Grant
The following authorization servers already support the requirements of the best practice:
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
AppAuth: OAuth Client Libraries for Apps
Google made the initial contribution of AppAuth for Android and iOS to the OpenID Foundation’s Connect WG.
Android library: http://openid.github.io/AppAuth-Android
iOS library: http://openid.github.io/AppAuth-iOS�
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
AppAuth for Android Codelab!
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Cordova
My team has open sourced a plugin for cordova to support in-app browser tabs on iOS and Android.
https://github.com/google/cordova-plugin-browsertab/
CIS Launch!
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Example Integration
AppAuth: Discovery
NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"];��[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer� completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {�� if (!configuration) {� NSLog(@"Error retrieving discovery document: %@", [error localizedDescription]);� return;� }�� // builds authentication request� OIDAuthorizationRequest *request =� [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration� clientId:kClientID� scopes:@[OIDScopeOpenID,� OIDScopeProfile]� redirectURL:KRedirectURI� responseType:OIDResponseTypeCode� additionalParameters:nil];� � // next slide... perform authorization request�}];
AppAuth: Making the Authorization Request
AppDelegate *appDelegate =� (AppDelegate *)[UIApplication sharedApplication].delegate;��appDelegate.currentAuthorizationFlow =� [OIDAuthState authStateByPresentingAuthorizationRequest:request� presentingViewController:self� callback:^(OIDAuthState *_Nullable authState,� NSError *_Nullable error) {� if (authState) {� NSLog(@"Got authorization tokens. Access token: %@", authState.lastTokenResponse.accessToken);� [self setAuthState:authState];� } else {� NSLog(@"Authorization error: %@", [error localizedDescription]);� [self setAuthState:nil];� }�}];
AppAuth: Declaring the redirect scheme
In the app’s XCode config:
A word on Redirect URI schemes
Choose a collision-resistant URI scheme like:
com.yourcompany.yourapp:/
�Avoid generic schemes like:
app:/
AppAuth: Receiving the OAuth Redirect
The response is delivered to the specified handler, and can be extracted from the intent data:
Code sample
- (BOOL)application:(UIApplication *)app� openURL:(NSURL *)url� options:(NSDictionary<NSString *, id> *)options {� // Sends the URL to the current authorization flow (if any) which will� // process it if it relates to an authorization response.� if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) {� _currentAuthorizationFlow = nil;� return YES;� }�� // Your additional URL handling (if any) goes here.�� return NO;�}
AppAuth: Making API Calls
[_authState withFreshTokensPerformAction:^(NSString *_Nonnull accessToken,� NSString *_Nonnull idToken,� NSError *_Nullable error) {� if (error) {� NSLog(@"Error fetching fresh tokens: %@", [error localizedDescription]);� return;� }�� // perform your API request using the tokens�}];
Read more at: http://openid.github.io/AppAuth-iOS/ and � http://openid.github.io/AppAuth-Android/
Given its flaws, and the viable alternatives, don’t assume Google will always support WebView for OAuth.
Thank You!
#AppAuth
William Denniss
@WilliamDenniss