Showing posts with label OAuth2. Show all posts
Showing posts with label OAuth2. Show all posts

Monday, January 12, 2015

Sharing Keychain access in a Share Extension

I've been wanted to do a blog post on how to achieve SSO on iOS using sharing Keychain for a bit...

And at the same time, I also wanted to try app extension very badly. So in an attempt to get the best of the two worlds, let's talk about writing a share extension to an app which need to store OAuth2 access token in a secure manner. We'll see how to share Keychain content through group-id between an app and its extension.

Remember Shoot'nShare app?
A simple app that takes pictures and allows you to share them with Facebook, GoogleDrive or even your own Keycloak backend. If we want to learn more about it, visit previous blog posts: To simplify, in this blog post we will focus on sharing to Google Drive only. As a pre-requisite, let's start creating a Google project.

OAuth2 Google Set up


If you want to create a google project to use for uploading files to Google Drive, follow the steps below:
  • Have a Google account
  • Go to Google cloud console, create a new project
  • Go to APIs & auth menu, then select APIs and turn on Drive API
  • Always in APIs & auth menu, select Credentials and hit create new client id button Select iOS client and enter your bundle id.
  • NOTES: Enter a correct bundle id as it will be use in URL schema to specify the callback URL. Please use your own unique BUNDLE_ID with format like org.YOUR_DOMAIN.Shoot replacing YOUR_DOMAIN with your actual domain.
Once completed you will have your information displayed as below:

Now that we've got your google project set up, let's add an Share Extension to Shoot app and see what's involved.

Share Extension


What it is?

An App extension add feature to an existing application. There are several types of extensions. The one we're interested in today is the share extensions. As the name says it all, this extension lets you share content with the external world. By default Xcode template will inherit from SLComposeServiceViewController. Therefore when hitting share button, a pop-up appears to send a message with image. Before iOS8, only a handset of providers were available to share content with. Those providers were defined directly in the operating system directly so the list was not flexible at all. Those days are over (yay!), you can now share with your favourite or even your own social networks directly from Photos app. This is exactly what we're going to do: let's share to GoogleDrive from Photos app via Shoot'nShare app.

One important thing to bear in mind extensions are not deployed by themselves. They must be packaged within a container app. Concretely in Xcode extensions are extension target within you container app.

Let's see an example

1. Get the project
Code source can be found in aerogear-ios-cookbook app.extension branch. Clone the repo and select the correct branch:
git clone git@github.com:aerogear/aerogear-ios-cookbook.git
git checkout AGIOS-224.shoot-extension
2. Define you own bundle_id
To be able to work with extension you need to enable App Groups. App Groups are closely linked to bundle identifiers. So let's change the BUNDLE_ID of the project to match your name. Select the Shoot project in the Project Navigator, and then select the Shoot target from the list of targets. On the General tab, update the Bundle Identifier to org.YOUR_DOMAIN.Shoot replacing YOUR_DOMAIN with your actual domain. Do the same for the extension target: select the Shoot project in the Project Navigator and then select the ShootExt target. On the General tab, update the Bundle Identifier to org.YOUR_DOMAIN.Shoot.ShootExt replacing YOUR_DOMAIN with your actual domain.

3. Configure App Group for Shoot target
In order for Shoot'nShare to share content with its extension, you’ll need to set up an App Group. App Groups allow access to group containers that are shared amongst related apps, or in this case your container app and extension. Select the Shoot project, switch to the Capabilities tab and enable App Groups by flicking the switch. Add a new group, name it group.org.YOUR_DOMAIN.Shoot, again replacing YOUR_DOMAIN with your actual domain.

4. Configure your App Group for ShootExt target
Open the Capabilities tab and enable App Groups. Select the group you created when setting up the Shoot project. The App Group simply allows both the extension and container app to share files. This is important because of the way files are uploaded when using the extension. Before uploading, image files are saved to the shared container. Then, they are scheduled for upload via a background task.

Sharing Keychain


With the same idea of sharing group between apps (or app and extension) to be able to have a common space for saving files, we can use Keychain group so that app and extension can share Keychain items. In our case we want a common space for Shoot app and Shoot Ext to share OAuth2 access token.

1. Configure Keychain Sharing for Shoot target
In order for Shoot'nShare to share access tokens with its extensions, you’ll need to set up a Keychain Sharing Group. Select the Shoot project in the Project Navigator, and then select the Shoot target from the list of targets. Now switch to the Capabilities tab and enable Keychain Sharing by flicking the switch. Add a new group, name it org.YOUR_DOMAIN.Shoot, again replacing YOUR_DOMAIN with your actual domain.

2. Configure Keychain Sharing for ShootExt target
Select the Shoot project in the Project Navigator and then select the ShootExt target. Open the Capabilities tab and enable Keychain Sharing. Select the group you created when setting up the Shoot project.

3. Configure Shoot App code
In Shoot/ViewController.swift modify:
@IBAction func shareWithGoogleDrive() {
        let googleConfig = GoogleConfig(
            clientId: "YOUR_GOOGLE_APP_ID.apps.googleusercontent.com",
            scopes:["https://www.googleapis.com/auth/drive"])
        let ssoKeychainGroup = "YOUR_APP_ID_PREFIX.org.YOUR_DOMAIN.Shoot"
...
where YOUR_APP_ID_PREFIX is a unique alphanumeric identifier, you can view it on dev center:
and org.YOUR_DOMAIN.Shoot is you BUNDLE_ID.

4. Configure ShootExt code
In ShootExt/ViewController.swift modify:
let ssoKeychainGroup = "357BX7TCT5.org.corinne.Shoot"
    let appGroup = "group.org.corinne.Shoot"

    override func didSelectPost() {      
        // We can not use googleconfig as per default it take your ext bundle id, here we want to takes shoot app bundle id for redirect_uri
        let googleConfig = Config(base: "https://accounts.google.com",
                authzEndpoint: "o/oauth2/auth",
                redirectURL: "org.YOUR_DOMAIN.Shoot:/oauth2Callback",
                accessTokenEndpoint: "o/oauth2/token",
                clientId: "YOUR_GOOGLE_APP_ID.apps.googleusercontent.com",
                refreshTokenEndpoint: "o/oauth2/token",
                revokeTokenEndpoint: "rest/revoke",
                scopes:["https://www.googleapis.com/auth/drive"])"
...
Replace:
the constant ssoKeychainGroup with your YOUR_APP_ID_PREFIX + BUNDLE_ID.
the constant appGroup with your App Group
in google config, redirectURL should match your BUNDLE_ID

5. Run the extension
To run shoot extension, select ShootExt target and run it, select Photos app as host app.
Select a photo, click on share button and select Shoot app. A Pop-up will appear, select send: you photo is uploaded on the background... and we're done. We've done all the configuration needed. Let's look at the code now.

Spot the Difference


Actually what we want to do from Share extension is basically the same as we do from Shoot'nShare app. But we do in an extension to allow us to do from Photos app. What about playing the difference game? What are the differences between uploading from Shoot'nShare app or uploading from ShootExt?

1. you can not trigger the OAuth2 danse from the extension
Extensions have limitations. Some API are not available. An app extension cannot access a sharedApplication object, and so cannot use any of the methods on that object. Difficult to trigger an external browser to launch the OAuth2 danse. Opening the container app in case no access tokens is available could be an alternative... However this alternative is offered only for today widget extension...

Indeed depending on extension type, some actions are allowed or forbidden. For example quoting apple doc : "only a today widget (and no other app extension type) can ask the system to open its containing app by calling the openURL:completionHandler: method of the NSExtensionContext class."

With our ShootExt, it would have been handy to be able to open Shoot'nShare app if no access token is available in the shared keychain. As our extension is a share extension, this is not available. As a result, we take as a pre-requisite that the end user has already shared a photo from Shoot'nShare app before using the extension. To do so we override OAuth2Module's requestAuthorizationCode method:
public class OAuth2ModuleExtension: OAuth2Module {
    // For extension we do not want to be redirected to browser to authenticate
    // As a pre-requisite we should have a valid access_token stored in Keychain
    override public func requestAuthorizationCode(completionHandler: (AnyObject?, NSError?) -> Void) {
        completionHandler("NO_TOKEN", nil)
    }
}
In case there is no token, we will return an error message to the end user asking him to use Shoot'nShare first.

2. you use the same redirect-uri OAuth2 for both extension and app
To do so, in ShootExt/ViewController.swift we can not use GoogleConfig class, we'll have to use Config class ans spscify shoot'nShare's redirect url as shown below:
let googleConfig = Config(base: "https://accounts.google.com",
                authzEndpoint: "o/oauth2/auth",
                redirectURL: "org.YOUR_DOMAIN.Shoot:/oauth2Callback",
                accessTokenEndpoint: "o/oauth2/token",
                clientId: "YOUR_GOOGLE_APP_ID.apps.googleusercontent.com",
                refreshTokenEndpoint: "o/oauth2/token",
                revokeTokenEndpoint: "rest/revoke",
                scopes:["https://www.googleapis.com/auth/drive"])


3. you need to save in the Keychain using group-id
When we first save the access token in Shoot'nShare app, we need to specified the group-id, in Shoot/Viewcontroller.swift, we modify shareWithGoogleDrive method to accomodate it: In line7-10 we create a TrustedPersistantOAuth2Session object with a keychain group-id:
 @IBAction func shareWithGoogleDrive() {
         let googleConfig = GoogleConfig(
            clientId: "YOUR_GOOGLE_APP_ID.apps.googleusercontent.com",
            scopes:["https://www.googleapis.com/auth/drive"])
        let ssoKeychainGroup = "YOUR_APP_ID_PREFIX.org.YOUR_DOMAIN.Shoot"
        // We specify the keychain groupId, should be the same as the one used in Share extension
        let gdModule = OAuth2Module(config: googleConfig, 
                                    session: TrustedPersistantOAuth2Session(accountId: 
                                               "ACCOUNT_FOR_CLIENTID_\(googleConfig.clientId)", 
                                               groupId: ssoKeychainGroup))
        self.http.authzModule = gdModule
        self.performUpload("https://www.googleapis.com/upload/drive/v2/files", parameters: self.extractImageAsMultipartParams())
    }


4. you upload your photo in the background
Last but not least, when dealing with extension, remember that action will take place in the background! In our case we want to perform a multipart upload (we're using multipart Google endpoint) in the background. using aerogear-ios-http you can perform multipart background upload either using upload method with stream or file as shown line 28. It's also possible to use a POST method with multipart params (behind the scene a NSURSLSession upload is performed):
 override func didSelectPost() {
        let googleConfig = ....
        
        // Create a TrustedPersistantOAuth2Session with a groupId for keychain group sharing
        let gdModule = OAuth2ModuleExtension(config: googleConfig, session: 
TrustedPersistantOAuth2Session(accountId: "ACCOUNT_FOR_CLIENTID_\(googleConfig.clientId)", 
groupId: ssoKeychainGroup))
        
        self.http.authzModule = gdModule
        gdModule.requestAccess { (response: AnyObject?, error: NSError?) -> Void in
            var accessToken = response as? String
            if accessToken == "NO_TOKEN" {
                println("You should go to Shoot app and grant oauth2 access")
            } else {
                let imageURL = self.saveImage(self.imageToShare!, name: NSUUID().UUIDString)
                // multipart upload
                let multiPartData = MultiPartData(url: imageURL!,
                            mimeType: "image/jpg")
                let parameters = ["file": multiPartData]
                // multi-part upload could be achievd either with upload as a stream or using POST
                self.http.upload("https://www.googleapis.com/upload/drive/v2/files", 
                         stream: NSInputStream(URL: imageURL!)!, 
                         parameters: parameters, 
                         method: .POST, 
                         progress: { (ar1:Int64, ar2:Int64, arr3:Int64) -> Void in
                    println("Uploading...")
                    }) { (response: AnyObject?, error: NSError?) -> Void in
                    println("Uploaded: \(response) \(error)")
                }

            }
        }
    }


Hope your find this blog post useful and remember: it's all about Sharing...
Do not hesitate to share this link :)

Thursday, November 13, 2014

OAuth2 for Android and iOS with Keycloak

Like Jo, our brave iOS developer, you might have bumped into OAuth2 when writing an app that posts messages on Facebook wall. All cool social apps need to go through OAuth2 or OpenID authentication and authorization. Why?
Because so far it is the only broadly adopted open standard. So you might have heard : "It's complicated etc..". Not really if you use the right tools.

Mary, a cool mobile developer too, knows all about OAuth2. Follow Jo and Mary in their coffee break chat to learn in 5 mins OAuth2 howtos.

What's the problem?



- So Mary, to start with what is the problem we're trying to solve with OAuth?

- Easy. On one hand you have services… in the form of APIs. For example, we have twitter API to get list of followers, tweets etc… Those APIS handle your confidential data. As far as your data are protect by a login/password account, all is fine. But on the other hand, you have lots of apps that need to consume those services. The question is simple: do you trust them all to share your twitter login/password with them?

- I don't want my password all over the Internet!!!

- Therefore, use OAuth. OAuth2 allows users to grant third-party access to their web resources without sharing their passwords. We talk about a “delegated access" between mobile and user resources using a security token called an "access token".

Different actors and grants



- Mary, very often I hear about OAuth2 dance, who's involved? What is it?

- Actually there are 4 actors: authorization server (see our police man), responsible of authentication and authorization by providing an access token. The resource server, responsible for serving resources checking if there a valid token. The resource-owner, the end-user of the app, it could be you. And the client, in our case the mobile app.

- What is the dance all about?

- The interactions between those actors is described in OAuth2 spec as grant flows. This is the dance. There are different grant flows to fit different client app needs. A mobile app has diff needs that a single page browser-based app. In the spec, there are 4 different flows, you can group them in 2 different families:
3-legged flows where you grant permission from user. It includes "implicit grant" for browser based app not capable of keeping tokens secure and the "authorization code grant".
2-legged flows where the credentials are given to the app. In "resources owner credentials", this is the login/password whereas "client credential grant" takes client_id and client_secret.

- How do I know which one to use?

- It actually all boils down to two questions: "Is your client app capable of securely storing access tokens?" and "What is your trust relationship with the client app?" In your case, Jo, you should go for "authorization code grant".

Authorization Code Grant

step0: registration


- One of the pre-requisites you need to go through Jo, is to register your mobile app. For example, you go to Google cloud dev console, you create a project, fill in the form (add your redirect URI). And eventually you get a client_id and client/secret

step1: authz code




- With this client_id you’re all set to develop your OAuth2 app. Your Shoot'nShare app will send a request (including the client_id and the redirect_uri its scope) to the third-party service asking for an Authorization code. The app switches to external browser.

- Do you have to switch context?

- Actually, there are different approaches, you can also embed a web view in your app. It's better from a usability perspective. But from a security point of view it's seen as less secure, as your app sits between the login/password form and the provider.

- With external browser, it's actually the real Facebook login page?

- Exactly. The user logs in (if not already logged in), authz server shows a grant page to the end-user: Shoot’nShare would like to access yours contacts and photos Allow/Deny. If the user clicks on "Allow", the server redirects to the client using the redirect_uri (the one we talked during step0) and sends the Authorization Code to the mobile application.

step2: exchange for access token


- Now that we’ve got this temporary code we can go to the token endpoint to exchange it for a proper access token.

step3: get resources


- Using this token, our mobile app can access protected resources on server. Here you can do your upload!

How to implement OAuth2 on iOS?

- Mary, if I want to start coding the iOS version of Shoot'nShare just after our coffee, what are my options:

- You could use Social.famework from Apple, it will fit well for Facebook and a couple of other providers but it’s limited to those providers defined in settings. Here you actually provide your login/password but you trust your OS don’t you?

- ...

- You could use Facebook sdk but same here, limited to Facebook. If you want to share your photo to Google+, you need to use Google sdk and so on… - But Mary, there must be an open source library to help us…

- Yes! actually let me tell you more about AeroGear OAuth2 library... OAuth2 is one of the lib offers by AeroGear mobile suite. All client sdk are declined in iOS, Android, Windows(work in progress) for native paltforms. There is also a JavaScript sdk (and its Cordova plugins when needed). AeroGear is not only client SDKs, it also offers server bits like its UPS (UnifiedPush Server), if you want to send push notification to iOS/Android/Windows.



Want to see it in action?

- Let's see it in action, follow me in this screencast:


OAuth2 Server Side

- Mary, I try to foresee my product owner needs, what about if he wants an OAuth2 secured rest endpoint, server side? - No worries, I've got an answer for you and it's all Open Source: Keycloak

Shootn'Share demo

- Let's see it in action, follow me in this screencast:



Any feedback please drop us a line on AeroGear mailing list or contact Keycloak mailing list for more in-depth question on OAuth2/SSO server.

Happy OAuth2!

Thursday, October 16, 2014

AeroGear with Keycloak, OAuth2 friends for iOS apps... running with Swift

You might have bumped into OAuth2 when writing an app that posts messages on Facebook wall. All cool social apps need to go through OAuth2 or OpenID authentication and authorization. Why?
Because so far it is the only broadly adopted open standard. So you might have heard : "It's complicated etc..". Not really if you use the right tools.

Want to see it in action?



Shootn'Share demo

You want to take cool photos and share them with friends using GoogleDrive or Facebook account? With Shoot'nShare you can take pictures, browse your camera roll, pick a photo and share it! Photos get uploaded to your GoogleDrive or Facebook wall. You can also run this demo with its associated Keycloak backend and upload photo to your own social network :]

The purpose of this blog is not to show you how to take picture in iOS, so let's work on existing Shoot'nShare. Just clone it from github:


git clone https://github.com/aerogear/aerogear-ios-cookbook
cd aerogear-ios-cookbook
git checkout swift
open Shoot/Shoot.xcodeproj

Shoot'nShare project

In this initial project you will find aerogear-ios-oauth2 as a source dependency in Shoot/libs/AeroGearOAuth2 the library we will use for OAuth2 on iOS.

Google setup (optional)

NOTES: This step is optional if your want to try the GoogleDrive app out of the box. You can reuse the Shoot client id for 'GoogleDrive'. However if you want to create your own app, you will have to go through your provider setup instruction. Here's how to do it for Google Drive.

1. Have a Google account
2. Go to Google cloud console, create a new project
3. Go to APIs & auth menu, then select APIs and turn on Drive API
4. Always in APIs & auth menu, select Credentials and hit create new client id button Select iOS client and enter your bundle id.

NOTES: Enter a correct bundle id as it will be use in URL schema to specify the callback URL.

Once completed, you will have your client id!

Shoot'nShare redirect URI for Google

Open Info.plist of your project as source code to see XML format and define
 CFBundleURLTypes
 
  
   CFBundleURLSchemes
   
         org.aerogear.Shoot
   
  
 
This URL has to be unique (making it match your bundle id ensures unicity) and has to match OAuth2 server side configuration.


Google sharing

Let's start by implementing sharing with Google. In Shoot/shoot/ViewController.swift go to shareWithGoogleDrive:
func shareWithGoogleDrive() {
        let googleConfig = GoogleConfig(
            clientId: "873670803862-g6pjsgt64gvp7r25edgf4154e8sld5nq.apps.googleusercontent.com",
            scopes:["https://www.googleapis.com/auth/drive"])
        
        let gdModule =  OAuth2Module(config: googleConfig)
        var http = Http()
        http.authzModule = gdModule
        
        gdModule.requestAccess { (response:AnyObject?, error:NSError?) -> Void in
            let filename = self.imageView.accessibilityIdentifier;
            let multiPartData = MultiPartData(data:UIImageJPEGRepresentation(self.imageView.image, 0.2),
                name: "image",
                filename: filename,
                mimeType: "image/jpg")
            http.POST("https://www.googleapis.com/upload/drive/v2/files", parameters: ["data": multiPartData], completionHandler: {(response, error) in
                if (error != nil) {
                    println("Error uploading file: \(error)")
                } else {
                    println("Successfully uploaded: " + response!.description)
                }
            })
        }
    }

In line 3 and 4, we use the client id (associated to Shoot Google app) and we specify the scope, here we share with google drive.

Line 6 and 7, we create an OAuth2 module. By default this module will create a TrustedSessionStorage to permanently store your tokens. Therefore every time your open your shoot app you can share photos without having to grant access everytime (see Notes below on iOS settings pre-requisites). You can choose a less secure MemorySessionStorage but each time you close your app and reopen it you will be prompted the first time to grant access.

Line 7 we initialize an http object and inject it the OAuth2 module.

In line 9, we actually request access. This method checks if the OAuth2Session (here stored in keychain) contains non-expired access token. If there is no token, it will go through the authorization code grant. If the token is expired (which happens every hour), a refreshed token will be asked transparently without any prompt.

NOTES: System requirement iOS8. Because this demo securely stores OAuth2 tokens in your iOS keychain, we've chosen to use WhenPasscodeSet policy for TrustedSessionStorage as a result to run this app you need to have your passcode set. For more details see WhenPasscodeSet blog post and Keychain and WhenPasscodeSet blog post.

Implicit http call

An even easier way to go, is to use aerogear-ios-http implicit grant. In the previous example we explicitly call requestAccess method. In Shoot/shoot/ViewController.swift change shareWithGoogleDrive by:
func shareWithGoogleDrive() {
       let googleConfig = GoogleConfig(
           clientId: "873670803862-g6pjsgt64gvp7r25edgf4154e8sld5nq.apps.googleusercontent.com",
           scopes:["https://www.googleapis.com/auth/drive"])
        let gdModule = AccountManager.addGoogleAccount(googleConfig)
        self.http.authzModule = gdModule
        self.http.POST("https://www.googleapis.com/upload/drive/v2/files", parameters:  self.extractImageAsMultipartParams(), completionHandler: {(response, error) in
            if (error != nil) {
                self.presentAlert("Error", message: error!.localizedDescription)
            } else {
                self.presentAlert("Success", message: "Successfully uploaded!")
            }
        })
   }

In line 5, we use AccountManager to create an OAuth2 Module an factory method to create OAuth2 module.

In line 7, we just post our image to Google without having ask for access. POST method underneath checks if an OAuth2 module is plugged to http and will make the right call for you:
  • either start authz code grant
  • or refresh access code if needed
  • or simply run the POST if all tokens are already available

  • OAuth2 with Keycloak

    Ready to build your own social network app, let's use Keycloak and build OAuth2 protected service...

    First of all, you can download Keycloak all appliance distribution.

    Then, clone Shoot backend repo:


    git clone https://github.com/aerogear/aerogear-backend-cookbook
    cd aerogear-backend-cookbook/Shoot


    Following README instructions, import shoot-realm into Keycloak, your admin console should look like:



    NOTE: Here too the redirect URI matches our bundle id. For Keycloak, you can put whatever you want but as we have Google config using bundle ID, let's reuse :)

    Similar to the other providers, you can create your Keycloak OAuth2 module using AccountManager, simply use addAccount with the class type of your OAuth2 module, as shown below:
    func shareWithKeycloak() {
            println("Perform photo upload with Keycloak")
            
            var keycloakConfig = Config(base: "http://localhost:8080/auth",
                authzEndpoint: "realms/shoot-realm/tokens/login",
                redirectURL: "org.aerogear.Shoot://oauth2Callback",
                accessTokenEndpoint: "realms/shoot-realm/tokens/access/codes",
                clientId: "shoot-third-party",
                refreshTokenEndpoint: "realms/shoot-realm/tokens/refresh",
                revokeTokenEndpoint: "realms/shoot-realm/tokens/logout")
    
            let gdModule = AccountManager.addAccount(keycloakConfig, moduleClass: KeycloakOAuth2Module.self)
            self.http.authzModule = gdModule
            self.performUpload("http://localhost:8080/shoot/rest/photos", parameters: self.extractImageAsMultipartParams())
        }

    And that's it! Go and check uploaded pictures in shoot web-app:



    Any feedback please drop us a line on AeroGear mailing list or contact Keycloak mailing list for more in-depth question on OAuth2/SSO server.

    Happy OAuth2!

    Saturday, September 27, 2014

    Authenticate with TouchID



    Last blog, you saw how to store sensitive data in keychain and how to trigger touchID authentication when accessing them. In this post, you'll see how to use TouchID API for local authentication. No Keychain here.

    I've used this KeychainWrapper in a small app KeychainTouchIDTest hosted on github very much inspired by the one used on session 711 of WWDC 2014. but this one is written in Swift ;)

    Passcode Set

    TouchId is used in complement to passcode feature. If you want to use it you will need to have your passcode set in your iOS8 settings.

    LocalAuthentication and TouchID

    In your AppDelegate.swift class, define promptTouchId method:
    func promptTouchID() {
            var myContext = LAContext()
            var authError: NSError? = nil
            var myLocalizedReasonString:String = "Authenticate using your finger"
            if (myContext.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error:&authError)) {   // [1]
                
                myContext.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics,         // [2]
                    localizedReason: myLocalizedReasonString,                              // [3]
                    reply: {(success: Bool, error: NSError!)->() in
                        
                        if (success) {
                            println("User authenticated")
                        } else {
                            switch (error.code) {
                            case LAError.AuthenticationFailed.toRaw():
                                println("Authentication Failed")
                                
                            case LAError.UserCancel.toRaw():
                                println("User pressed Cancel button")
                                
                            case LAError.UserFallback.toRaw():                               // [4]
                                println("User pressed \"Enter Password\"")
                                
                            default:
                                
                                println("Touch ID is not configured")
                            }
                            
                        println("Authentication Fails");
                        let alert = UIAlertView(title: "Error", message: "Auth fails!", delegate: nil, cancelButtonTitle:"OK")
                        alert.show()
                    }
                })
            } else {
                println("Can not evaluate Touch ID");
                let alert = UIAlertView(title: "Error", message: "Your passcode should be set!", delegate: nil, cancelButtonTitle:"OK")
                alert.show()
            }
        }
    
    In [1], you first check wether passcode is set and touchId feature is available on this device.

    In [2], you call the authentication process. DeviceOwnerAuthenticationWithBiometrics is an enum which (for now) contains only one value. But future will open room for other means of authentication, let's keep an eye on WWDC next sessions.

    You can add an additional string in [3] which describes why you are doing this operation. You should always inform the user why he is prompted for Touch ID authentication.

    Allow a user fallback in [4]. Your user might want to authenticate by other means than finger recognition. You will have to implement your own password strategy, passcode fallback is not yet available on programmable API.

    To prompt at start-up, call promptTouchID method from application:didFinishLaunchingWithOptions: in AppDelegate:
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            promptTouchID()
            return true
        }
    
    I hope this serie of three posts: helped you get a view on what's new on TouchID and Keychain APIs on iOS8. Feel free to share your experience with it, I'd love to hear it.

    Happy iOS8!

    Sunday, September 14, 2014

    TouchID and Keychain, iOS8 best friends

    Last blog, we saw how to store sensitive data in iOS keychain and the different problems when switching from passcode on to passcode off set. Default access to keychain WhenUnlocked does not prevent access to keychain items when the device is not protected by passcode any more. In iOS8 with the new attribute kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, when trying to access keychain items, we end up with an error message if the device configuration set to passcode off. In this post, we'll go a step further in securing access to our sensitive data every times you access them. Welcome to TouchId.

    TouchID: What is it?

    It's a fingerprint recognition feature and is only available on the iPhone 5S and plus. Fingerprint data is stored on the secure enclave of the Apple A7 processor that is inside the device itself. To read more the existing and mysterious topic of secure enclave check apple security paper here.

    If the user's phone has been rebooted, or has not been unlocked for 48 hours, only the user's passcode, not a fingerprint, can be used to unlock the phone.

    Since iOS8, you can programatically use touchID APIs to:
  • either do local authentication,
  • or store in Keychain
  • Let's revisit our previous example hosted on github. We had the option to add an item in Keychain, update and read it. But we want to make sure every times the app reads the item the user is required to authenticate via touchID.

    Keychain Access with TouchID

    The existing ACL attributes on keychain:
  • kSecAttrAccessGroup: is used for WHAT are the apps which can access it. If you want the new keychain item to be shared among multiple applications, include the kSecAttrAccessGroup key
  • kSecAttrAccessible: expresses WHEN the user can access it. Among the different options: WhenUnlocked and the newbie WhenPascodeSet.
  • kSecAttrAccessControl: is for GRANTing. This is a new iOS8 attribute. It allows you to define a fine-grained access control. You use it with the method SecAccessControlCreateWithFlags. For now the enum contains only one value (but there will be room for more configuration) "UserPresence".
  • Let's code it, we're going to replace the generic createQuery by a more specific createQueryForAddItemWithTouchID for adding an item and createQueryForReadItemWithTouchID for retrieving it:
    func createQueryForAddItemWithTouchID(# key: String, value: String? = nil) -> NSMutableDictionary {
            var dataFromString: NSData? = value?.dataUsingEncoding(NSUTF8StringEncoding)
            var error:  Unmanaged?
            var sac: Unmanaged
            sac = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                        kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .UserPresence, &error)
            let retrievedData = Unmanaged.fromOpaque(sac.toOpaque()).takeUnretainedValue()
            
            var keychainQuery = NSMutableDictionary()
            keychainQuery[kSecClass] = kSecClassGenericPassword
            keychainQuery[kSecAttrService] = self.serviceIdentifier
            keychainQuery[kSecAttrAccount] = key
            keychainQuery[kSecAttrAccessControl] = retrievedData
            keychainQuery[kSecUseNoAuthenticationUI] = true
            if let unwrapped = dataFromString {
                keychainQuery[kSecValueData] = unwrapped
            }
            return keychainQuery
        }
    
    Line 4, we create an AccessControl object with the policy set to kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Note the flag item is set to the only possible UserPresence. we set the attribute kSecUseNoAuthenticationUI because we don't want to be prompted on Add.

    Read Keychain with TouchID popping up

    For the read, we only need to customize the pop-up window content with the attribute kSecUseOperationPrompt.
    func createQueryForReadItemWithTouchID(# key: String, value: String? = nil) -> NSMutableDictionary {
            var dataFromString: NSData? = value?.dataUsingEncoding(NSUTF8StringEncoding)
    
            
            var keychainQuery = NSMutableDictionary()
            keychainQuery[kSecClass] = kSecClassGenericPassword
            keychainQuery[kSecAttrService] = self.serviceIdentifier
            keychainQuery[kSecAttrAccount] = key
    
            keychainQuery[kSecUseOperationPrompt] = "Do you really want to access the item?"
            keychainQuery[kSecReturnData] = true
            
            return keychainQuery
        }
    
    Things to remember

    Accessibility and AccessControl work together. One key implication of using TouchID and Keychain is: the user has to authenticate using standard UI, therefore the app must be in foreground. Be aware that broad queries on Keychain may request items that need user auth. Last but not least, the keychains items stored using kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly are not synchronised or back-up on iCloud.

    That's all for today, next blog post we can see how to use TouchID for LocalAuthentication and how to fallback when touchID is not available. Stay tuned!

    Happy iOS8, Happy Swift!

    Friday, September 12, 2014

    New kids on the block: WhenPasscodeSet policy on iOS8

    ... oki, its real name, a bit less glamour, is kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly.

    TL;TR: What is it all about? A cool option which let you save sensitive data on Keychain only if the user's device configuration is set with passcode on. It's up to Keychain to deal with configuration changes (ie: user changes his phone's configuration from passcode on to passcode off) in a secure way.

    Keychain

    Keychain is a database, whose rows are called Keychain items. Keychain items have values, and those values are encrypted. You define keychain with attributes to later help you do queries. Typically Keychain is used to store passwords or encryption key, not large amount of data.

    Keychain exists in OS X version and iOS version. Those versions are quire different. Here we're going to focus on iOS.

    iOS Keychain

    In iOS, an application always has access to its own keychain items and does not have access to any other application’s items. The system generates its own password for the keychain (no prompt for password), and stores the key on the device in such a way that it is not accessible to any application.

    When a user backs up iPhone data, the keychain data is backed up but the secrets in the keychain remain encrypted in the backup. The keychain password is not included in the backup. Therefore, passwords and other secrets stored in the keychain on the iPhone cannot be used by someone who gains access to an iPhone backup.

    All keychain items are protected by user's passcode plus device secret (unique secret for each UID only known by the device itself). An encrypted iCloud backup is available in case of stolen device.

    Keychain wrapper

    Here is a simple KeychainWrapper, that let you save a key/value item, in Swift, of course :)
    public class KeychainWrap {
        public var serviceIdentifier: String
        
        public init() {
            if let bundle = NSBundle.mainBundle().bundleIdentifier {
                self.serviceIdentifier = bundle
            } else {
                self.serviceIdentifier = "unkown"
            }
        }
        
        func createQuery(# key: String, value: String? = nil) -> NSMutableDictionary {
            var dataFromString: NSData? = value?.dataUsingEncoding(NSUTF8StringEncoding)
            var keychainQuery = NSMutableDictionary()
            keychainQuery[kSecClass] = kSecClassGenericPassword
            keychainQuery[kSecAttrService] = self.serviceIdentifier
            keychainQuery[kSecAttrAccount] = key
            //keychainQuery[kSecAttrAccessible] = kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
            if let unwrapped = dataFromString {
                keychainQuery[kSecValueData] = unwrapped
            } else {
                keychainQuery[kSecReturnData] = true
            }
            return keychainQuery
        }
        
        public func addKey(key: String, value: String) -> Int {
            var statusAdd: OSStatus = SecItemAdd(createQuery(key: key, value: value), nil)
            return Int(statusAdd)
        }
        
        public func updateKey(key: String, value: String) -> Int {
            let attributesToUpdate = NSMutableDictionary()
            attributesToUpdate[kSecValueData] = value.dataUsingEncoding(NSUTF8StringEncoding)!
            var status: OSStatus = SecItemUpdate(createQuery(key: key, value: value), attributesToUpdate)
            return Int(status)
        }
        
        public func readKey(key: String) -> NSString? {
            
            var dataTypeRef: Unmanaged?
            let status: OSStatus = SecItemCopyMatching(createQuery(key: key), &dataTypeRef)
            
            var contentsOfKeychain: NSString?
            if (Int(status) != errSecSuccess) {
                contentsOfKeychain = "\(Int(status))"
                return contentsOfKeychain
            }
            
            let opaque = dataTypeRef?.toOpaque()
            if let op = opaque? {
                let retrievedData = Unmanaged.fromOpaque(op).takeUnretainedValue()
                // Convert the data retrieved from the keychain into a string
                contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
            } else {
                println("Nothing was retrieved from the keychain. Status code \(status)")
            }
            
            return contentsOfKeychain
        }
    }
    
    Here we defined addKey, updateKey and readKey. I also provide a resetAll method (not shown in code snippet but use source code for reference). As line 18 is commented out, we're on kSecAttrAccessibleWhenUnlocked accessibility mode, the one per default.

    I've used this KeychainWrapper in a small app KeychainTouchIDTest hosted on github very much inspired by the one used on session 711 of WWDC 2014. Now it's time to play with it and see how it behaves...

    Let's play On/Off game

    WhenUnlocked policy for keychain (with line 18 commented)
    • in Settings -> TouchID & Passcode, choose "Turn passcode on"
    • start KeychainTouchIDTest
    • click Add item, should return success
    • click Query item, should return success
    • in Settings -> TouchID & Passcode, choose "Turn passcode off"
    • switch to KeychainTouchIDTest
    • click Add item, should return success
    • click Query item, should return success
    Here you touch the problem, you initially saved your credit car number in a very secure place (keychain), your phone being secured with passcode but as you remove authentication (passcode off) from you phone configuration, what happens?

    You left the door wide open. Anyone can take you phone and use your credit card! Let's carry on doing some more testing to see how WhenPasswordSet behaves...

    WhenPasscodeSet policy for keychain (with line 18 uncommented)
    • in Settings -> TouchID & Passcode, choose "Turn passcode on"
    • switch to KeychainTouchIDTest
    • click reset all
    • click Add item, should return success
    • click Query item, should return success
    • in Settings -> TouchID & Passcode, choose "Turn passcode off"
    • click Query item, should return not found error
    • click Add item, should return error
    Much more secure.
    Since you changed your passcode configuration to off, Keychain has securely removed the sensitive data. Switching it back to passcode on will not restore them. They're gone for good.

    One step further: TouchID

    Suppose you want to authenticate every times this sensitive data is accessed. Let's say it's a credit card information and you want to approve all transaction personally. In iOS8 it's possible using WhenPasscodeSet's little friend API: TouchID!

    I won't spoil the pleasure after a too long blog, this will be for a second post. Stay tuned!

    Wednesday, June 4, 2014

    Different ways to manage Facebook OAuth2 for iOS

    Take this old Hinduism saying: "There is one truth, reached by many paths". Very often there are many ways to achieve the same end result. In our OAuth2 blog serie, we've seen how to use OAuth2 to grant access to GoogleDrive, how to transparently renew grant after expiration time, let's now focus on another OAuth2 main provider: Facebook.

    Let's explore different ways of accessing your Facebook wall.

    Let's start with ...

    Using Social.framework

    Since iOS 6, Apple added the support of Facebook in its Social.framework. With the built-in support, end user has to register their Facebook account into iOS settings and grant access.
    - (IBAction)postToFacebook:(id)sender {
        
        ACAccountStore *accountStore = [[ACAccountStore alloc] init];
        ACAccountType *facebookAccountType = [accountStore
                                              accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
        
        __block ACAccount *facebookAccount;
        // Specify App ID and permissions
        NSDictionary *options = @{ACFacebookAppIdKey: @"xxxxx",                        // [1]
                                  ACFacebookPermissionsKey: @[@"publish_actions"],     // [2]
                                  @"ACFacebookAudienceKey": ACFacebookAudienceFriends};
        
        [accountStore requestAccessToAccountsWithType:facebookAccountType
                                              options:options completion:^(BOOL granted, NSError *e) {
                                                  if (granted) {                      // [3]
                                                      NSArray *accounts = [accountStore
                                                                           accountsWithAccountType:facebookAccountType];
                                                      facebookAccount = [accounts lastObject];
                                                      // Get the access token, could be used in other scenarios
                                                      ACAccountCredential *fbCredential = [facebookAccount credential];
                                                      NSString *accessToken = [fbCredential oauthToken];
                                                      
                                                      NSLog(@"...Facebook Access Token: %@", accessToken);
                                                      
                                                  } else {
                                                      // Handle Failure
                                                  }
                                              }];
       
        if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {   // [4]
            SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
            
            [controller setInitialText:@"First post from my iPhone app"];
            [self presentViewController:controller animated:YES completion:Nil];
        }
    }
    

    In [1], you specify your Facebook app id and you specify the permissions needed [2].
    In [3], you're in the callback from the authorization. As there is only one callback you need to test for successful grant or not. You can then embed your logic in it. As an exemple I show how you can retrieve access token and just logged them.
    In [4], you can wait until the asynch authorization request has ended and do you Facebook post using SLComposeViewController. Note when doing you POST request it's the framework itself that pass the access token into the HTTP header. Easy peasy nothing to do on your side.

    Pro and cons:
    As a pre-requisite, the end user need to be registered in Setting
    Facebook credentials are stored in your phone
    Only build-in support provider like Twitter, Facebook (for now)
    Note all Facebook actions are supported, for more advanced features you will need to go the 2 other ways.

    Using Facebook iOS SDK

    The good part of Facebook SDK is that it comes bundled with a bunch of good exemples, install it and follow the instruction as described here. For the purpose of our comparison exercice I've chosen to extract some code snipet from the HelloFacebookSample which shows you how handle OAuth2 authenticate and authorize using external browser. To deal with going back to main app from external browser, you work with URL schema and you need the following setting in your property file:
     CFBundleURLTypes
     
      
       CFBundleURLSchemes
       
        fbCLIENT_APP_ID
       
      
     
    
    
    The Facebook SDK provides you predefined view and controller to achieve the different actions. For example with FBLoginView you can create a login button. Like shown below:
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // Create Login View so that the app will be granted "status_update" permission.
        FBLoginView *loginview = [[FBLoginView alloc] init];
        loginview.frame = CGRectOffset(loginview.frame, 5, 5);
        loginview.delegate = self;
        [self.view addSubview:loginview];
        [loginview sizeToFit];
    }
    
    Implementing the FBLoginViewDelegate delegate, you can implement the callback method as required. Notice here we use a delegate vs a callback block approach for async call. My preference goes toward block if you remember my previous blog post, but yeah just a matter of preferences ;)
    @protocol FBLoginViewDelegate 
    - (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView;
    
    - (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
                                user:(id)user;
    
    - (void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView;
    
    - (void)loginView:(FBLoginView *)loginView
          handleError:(NSError *)error;
    
    @end
    
    Pro and cons:
    Another SDK to learn,
    a Facebook specific dependency to add

    Using AeroGear

    Using AeroGear iOS OAuth2 adapter, you can log in to any OAuth2 provider, don't need to include Facebook iOS sdk or Social.framework. The main advantage is if your Shoot'nShare app wants to share a photo to different providers like Google+, Facebook you don't need to work with each sdk.
    -(void)oauthFacebook {
        // start up the authorization process
        AGAuthorizer* authorizer = [AGAuthorizer authorizer];
        
        // TODO replace XXX -> secret and YYY -> your app id in this file + plist file
        id _facebookAuthzModule = [authorizer authz:^(id config) {  // [1]
            config.name = @"restAuthMod";
            config.baseURL = [[NSURL alloc] init];
            config.authzEndpoint = @"https://www.facebook.com/dialog/oauth";
            config.accessTokenEndpoint = @"https://graph.facebook.com/oauth/access_token";
            config.clientId = @"YYY";
            config.clientSecret = @"XXX";
            config.redirectURL = @"fbYYY://authorize/";
            config.scopes = @[@"user_friends, publish_actions"];
            config.type = @"AG_OAUTH2_FACEBOOK";
        }];
        [_facebookAuthzModule requestAccessSuccess:^(id response) {              // [2]
            [self shareWithFacebook];
            NSLog(@"Success to authorize %@", response);
            
        } failure:^(NSError *error) {
            NSLog(@"Failure to authorize");
        }];
    }
    
    In [1], you configure your OAuth2 provider. In [2], you request the grant access. If this is the first time, the app open an external browser to start OAuth2 danse. Enter login/password if not already logged and grant access. Once access is granted you will be forwarded back to Shoot'nshare app using URL schema. The main advantage of using external browser rather than embedded view is that it's keep your login/password safe. No code in between embedded view and client app that you can't control.
    Some configuration is needed in your Shoot-Info.plist for URL schema:
        CFBundleURLTypes
        
            
                CFBundleURLName
                fb240176532852375
                CFBundleURLSchemes
                
                    fb240176532852375
                
            
        
    
    Ready to actually work with Pipe objects, also part of AeroGear iOS, Pipe is a connection abstraction that let you do CRUD operation asynchronously over the network:
    -(void)shareWithFacebook {
            // extract the image filename
            NSString *filename = ...;
            
            // the Facebook API base URL, you need to
            NSURL *gUrl = [NSURL URLWithString:@"https://graph.facebook.com/me/"];
            
            AGPipeline* gPipeline = [AGPipeline pipelineWithBaseURL:gUrl];
            
            // set up upload pipe
            id uploadPipe = [gPipeline pipe:^(id config) {   // [1]
                [config setName:@"photos"];
                [config setAuthzModule:_facebookAuthzModule];
            }];
            
            // Get currently displayed image
            NSData *imageData = ...;
            
            // set up payload with the image
            AGFileDataPart *dataPart = [[AGFileDataPart alloc] initWithFileData:imageData
                                                                           name:@"image"
                                                                       fileName:filename
                                                                       mimeType:@"image/jpeg"];
            NSDictionary *dict = @{@"data:": dataPart};
            
            // show a progress indicator
            [uploadPipe setUploadProgressBlock:^(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [SVProgressHUD showProgress:(totalBytesSent/(float)totalBytesExpectedToSend) status:@"uploading, please wait"];
                });
            }];
            
            // upload file
            [uploadPipe save:dict success:^(id responseObject) {   // [2]
                [SVProgressHUD showSuccessWithStatus:@"Successfully uploaded!"];
            } failure:^(NSError *error) {
                [SVProgressHUD showErrorWithStatus:@"Failed to upload!"];
            }];
        }
    
    
    In [1] I just instantiate a Pipe that I can use to save my picture [2].
    Pros and cons:
    AeroGear OAuth2 dependency to add but cross providers
    More verbose in term of configuration but you can do whatever you want once you get the access token, it's just plain HTTP REST calls.

    You can find the complete source code in aerogear-ios-cookbook git repo, going to the Shoot recipe.

    Conclusion

    Because I'm core committer on the AeroGear project, my view is biased of course. I'd go for the AeroGear way :) the main reason being it gives you the most flexibility without having to link to several SDKs. The built-in solution is also very tempting but limited besides the fact it's required the end user a previous registration.
    Many ways to achieve the same end result, and looking deeper you may find other ways. I'm curious to hear about your discoveries and how we could improve AeroGear iOS libs. Share your thoughts, email AeroGear.

    Monday, June 2, 2014

    OAuth2 Refresh

    We've been talking in length about authorization code grant in my last previous posts.

    If we go back to you GoogleDrive exemple (remember OAuth2 discussion part2) let's see how we can implement refresh grant. From OAuth2 spec, if you request an authorization code grant, you should have received a refresh token at the same time you got your access token. Access token expired in 1 hour (for Google for ex) and if you don't want to prompt again for granting access your end-users, you need to use refresh token.

    What are they for?

    Quoting from ietf mailing list:
    There is a security reason, the refresh_token is only ever exchanged with authorization server whereas the access_token is exchanged with resource servers. This mitigates the risk of a long-lived access_token leaking (query param in a log file on an insecure resource server, beta or poorly coded resource server app, JS SDK client on a non https site that puts the access_token in a cookie, etc) in the "an access token good for an hour, with a refresh token good for a year or good-till-revoked" vs "an access token good-till-revoked without a refresh token." Long live tokens are seeing as a security issue whereas short live token + refresh token mitigates the risk.

    How to ask?

    Refreshing an access token is trivial, it's just a matter of sending you refresh token with an grant_type set to refresh_token.

    Not always available...

    Facebook for instance don't go the OAuth2 way. After dropping support for long live access token in offline mode, Facebook goes the path of exchanging short lived access token for long lived user access token (60 days). Not quite a refresh_token. The long lived_token does expired too whereas the refresh token lives until revoked.

    Going back to code

    Remember GoogleDrive client app which main goal is to connect to google drive and retrieve a list of document. Let's see how AeroGear OAuth2 deal with refreshing tokens.

    In the main ViewController file, AGViewController.m:
        AGAuthorizer* authorizer = [AGAuthorizer authorizer];
        
        _restAuthzModule = [authorizer authz:^(id config) {      [1]
            config.name = @"restAuthMod";
            config.baseURL = [[NSURL alloc] initWithString:@"https://accounts.google.com"];
            config.authzEndpoint = @"/o/oauth2/auth";
            config.accessTokenEndpoint = @"/o/oauth2/token";
            config.revokeTokenEndpoint = @"/o/oauth2/revoke";
            config.clientId = @"XXX";
            config.redirectURL = @"org.aerogear.GoogleDrive:/oauth2Callback";
            config.scopes = @[@"https://www.googleapis.com/auth/drive"];
        }];  
    
        NSString* readGoogleDriveURL = @"https://www.googleapis.com/drive/v2";
        NSURL* serverURL = [NSURL URLWithString:readGoogleDriveURL];
        AGPipeline* googleDocuments = [AGPipeline pipelineWithBaseURL:serverURL];
    
        id documents = [googleDocuments pipe:^(id config) {       [2]
            [config setName:@"files"];
            [config setAuthzModule:authzModule];                                        [3]
        }];
    
        [documents read:^(id responseObject) {                                          [4]
            _documents = [[self buildDocumentList:responseObject[0]] copy];
            [self.tableView reloadData];
        } failure:^(NSError *error) {
            // when an error occurs... at least log it to the console..
            NSLog(@"Read: An error occurred! \n%@", error);
        }];
    
    

    In [1], Create an Authorization module, in [2] create a Pipe to read your Google Drive documents, injecting the authzModule into the pipe, and then we simply read the pipe!
    You don't have to explicitly call requestAccessSuccess:failure: before reading a Pipe associated to an authzModule. If you don't call it the request will be done on your first CRUD operation on the pipe. However if you prefer to control when you want to ask the end-user for grant permission, you can call it explicitly.

    Behind the hood, what is AeroGear doing for us?

    Inside AeroGear iOS code

    When you call a Pipe.read, AeroGear implicitly wrapped this call within a requestAccessSuccess:failure: call. But what does this method do? well it depends on you state...
    -(void) requestAccessSuccess:(void (^)(id object))success
                         failure:(void (^)(NSError *error))failure {
        if (self.session.accessToken != nil && [self.session tokenIsNotExpired]) {
            // we already have a valid access token, nothing more to be done
            if (success) {
                success(self.session.accessToken);
            }
        } else if (self.session.refreshToken != nil) {
            // need to refresh token
            [self refreshAccessTokenSuccess:success failure:failure];
        } else {
            // ask for authorization code and once obtained exchange code for access token
            [self requestAuthorizationCodeSuccess:success failure:failure];
        }
    }
    
    If you already have valid token, you're fine just forward success, if your access token has expired but you have a refresh token, just go for a refresh and last if you don't have any them, you need to go for the full grant pop-up.

    Don't want to ask grant at each start-up

    Ok fine, I have a mechanism to be able to ask only once at each client start up for access token and refresh token, then if I don't want to ask each time I start the app. Storing the tokens seem the way to go... I'll tell you more about AGAccountManager in my next blog post and how you can safely store your tokens.

    To see complete source code for GoogleDrive app go to aerogear-ios-cookbook.

    Tuesday, May 20, 2014

    OAuth2 grant flows

    OAuth2 what is it for?

    OAuth 2.0 is an authorization framework commonly used to grant applications limited access to a user's resources without exposing the users credentials to the application.

    As simple as that. But guess what, there isn't just one way of granting. Let's visit the different OAuth2 grant types as described in the spec.

    To go a bit deeper in details from my previous post on OAuth2-discussion-part1, I would list 4 actors:
    • authz server,
    • resource server, those two are generally separate server from your OAuth2 provider, the later responsible for serving resource checking if there a valid token the former, giving authorisation by providing the hop access token,
    • user (resource owner),
    • client (mobile app).
    The most common flow is based on Authorisation code grant. See it explained in my last post with GoogleDrive as an exemple.

    Delving deeper in OAuth2 specification, there is several grant types in order to provide an access token. From the spec, we can extract four authorisation grants:
    • Authorisation code grant
    • Implicit grant
    • Resource owner credentials grant
    • Client credentials grant

    Authorization Code Grant

    As said earlier, without doubt the most popular one and most probably the one you need ;)

    On native app, this flow can be achieved either using an embedded browser or redirect the user to the native browser and then are redirected back to the app using a custom protocol (in iOS land, we use URL Schemes). For AeroGear-iOS OAuth2 library we use the latter. We’ll discuss more in details why in next blog post.

    The authorisation code grant type is used to obtain both access tokens and refresh tokens. In next blog post, I shall also talk in detail about refreshing token flow;

    Implicit grant

    Ok this one target pure “web” app. From the spec: “In the implicit flow, instead of issuing the client an authorization code, the client is issued an access token directly (as the result of the resource owner authorization)”.

    With the implicit grant you get straight an access token, but you don’t get refresh token. BTW, you can find an exemple of implementation in AeroGear-JS with its demo GoogleDrive cookbook recipe.

    Resource Owner Password Credentials Grant

    When this grant is implemented the client itself will ask the user for their username and password to get an access token. This is used for trusted client.

    Client credentials grant

    A variant of the previous one, except only the client credentials are used. Similar use cases. For exemple, Twitter decided to implement client credentials OAuth2 type.

    What does it mean?
    • if your application only shows tweets from other users, you can get authorized using OAuth 2.
    • but if you want any users to use your app to post tweets or do anything else on a user's behalf, you should stick to OAuth 1.0a Twitter API.

    Here are the 4 basic grant types. At those, you can add Refresh token grant and the extension grant (MAC token, JSON web, SAML). And of course, how much a provider is OAuth2 compliant is also source of variants...
    But that will be the subject of another post ;)

    Wednesday, January 8, 2014

    OAuth2 discussion - part2

    We talked about OAuth2 protocol in part 1, let's see it in action in this second part. We'll take a demo app from aerogear-ios-cookbook and I'm going to drive you through it step by step.

    GroogleDrive app displays the list of documents in your Google Drive using OAuth2 to authorize with aerogear-ios library.

    Let's do Google setup

    Before we start you will need to register your app, follow the steps below:
    1. Have a Google account
    2. Go to Google cloud console, create a new project
    3. Go to APIs & auth menu, then select APIs and turn on Drive API
    4. Always in APIs & auth menu, select Credentials and hit create new client id button Select iOS client and enter your bundle id. Enter a correct bundle id as it will be use in URL schema (if you don't know about URL Schemas I suggest you read about it) to specify the callback URL.

    Once completed you will have your information displayed as below:



    You'll get a Client Id, a Client Secret and a callback URL, open Xcode (or AppCode whathever you like best), go to GoogleDrive-Info.plist and add an new URL schema entry as shown below:



    Show me the code

    In AGViewController.m:

    - (IBAction)authorize:(UIButton *)sender {
        AGAuthorizer* authorizer = [AGAuthorizer authorizer];
    
        _restAuthzModule = [authorizer authz:^(id config) {              [1]
            config.name = @"restAuthMod";
            config.baseURL = [[NSURL alloc] initWithString:@"https://accounts.google.com"];
            config.authzEndpoint = @"/o/oauth2/auth";
            config.accessTokenEndpoint = @"/o/oauth2/token";
            config.clientId = @"XXXXX";
            config.redirectURL = @"org.aerogear.GoogleDrive:/oauth2Callback";
            config.scopes = @[@"https://www.googleapis.com/auth/drive"];
        }];
    
        [_restAuthzModule requestAccessSuccess:^(id object) {                           [2]
            [self fetchGoogleDriveDocuments:_restAuthzModule];                          [4]
        } failure:^(NSError *error) {
        }];
    }
    
    [1]: configuration with all required URLs and endpoints.
    [2]: requestAccessSuccess:failure: is the method dealing with all the steps needed to authorize. Within this method, we're going to bring the UI grant web page, once authorization is granted, we go back to the mobile app and then we exchange code for access token. this is the heart of OAuth2 dance ;)

    In AGAppDelegate.m:

    - (BOOL)application:(UIApplication *)application
                openURL:(NSURL *)url
      sourceApplication:(NSString *)sourceApplication
             annotation:(id)annotation
    {                                                                                   [3]
        NSNotification *notification = [NSNotification notificationWithName:@"AGAppLaunchedWithURLNotification" 
        object:nil userInfo:[NSDictionary dictionaryWithObject:url forKey:@"UIApplicationLaunchOptionsURLKey"]];
        [[NSNotificationCenter defaultCenter] postNotification:notification];
    
        return YES;
    }
    
    [3]: once popup has been answered, go back to GoogleDrive app and notified AeroGear framework. If this method is not well implemented, the browser won't be able to callback the application. Back to the app, access code is excahnge with access token (step 2).
    [4]: the success callback takes the access token as parameter. The access token is stored in memory with AGAuthzModule. It's up to the developer to store permanently the access token for next app launch. Note that access token has an expiration date (1h for Google).

    In AGViewController.m:

    -(void)fetchGoogleDriveDocuments:(id) authzModule {
        NSString* readGoogleDriveURL = @"https://www.googleapis.com/drive/v2";
        NSURL* serverURL = [NSURL URLWithString:readGoogleDriveURL];
        AGPipeline* googleDocuments = [AGPipeline pipelineWithBaseURL:serverURL];
    
        id documents = [googleDocuments pipe:^(id config) {       [5]
            [config setName:@"files"];
            [config setAuthzModule:authzModule];                                        [6]
        }];
    
        [documents read:^(id responseObject) {                                          [7]
            _documents = [[self buildDocumentList:responseObject[0]] copy];
            [self.tableView reloadData];
        } failure:^(NSError *error) {
            // when an error occurs... at least log it to the console..
            NSLog(@"Read: An error occured! \n%@", error);
        }];
    }
    
    [5]: create your Pipeline and Pipe objects as usual.
    [6]: setting authz module in your pipe configuration will pass the access token for each pipe operations transparently.
    [7]: read the pipe as usual.

    As a conclusion

    No need to learn and use different providers SDK, Aerogear-iOS library allows you to use OAuth2 with any providers. Once you're authorized, you can use the pipes transparently (no need for you to pass the access token, the framework does it for you).
    OAuth2 will be shipped in 1.4.0. Give it a trial, give us your feedback and enjoy!

    Monday, January 6, 2014

    OAuth2 discussion - part1

    Sometimes pronounced with one syllable "oath", but I like it better with two syllables "Oh Auth", add a 2 at the end and here is OAuth2, an open standard for authorization. It allows users to share their private resources (e.g. photos, documents etc...) stored on one site with another site or application without having to hand out their credentials. Widely adopted from Google to Facebook, easy and less cumbersome than his elder brother OAuth1, let's see together how to practice the OAuth2 dance.

    Actually, the dance comparison is often used to describe OAuth2, but I think it's more a 3 players discussion so we're going to use a script metaphor. Here is our casting: Mr Google (our cloud based service provider), MyGoogleDrive app (our native mobile app which main functionality is to list Google Drive files) and Bob our end user who want to access his Google drive content via MyGoogleDrive app. We want to write a native iOS client app using third party backend.

    Before we start, MyGoogleDrive developer has already registered his app to consume Google Drive services.  Each provider offers an admin console: GoogleCloud console... Depending on provider, you can choose want kind of access you need (read/write). Once registered you should get a client id, a client secret and a callback URI.

    Bob starts MyGoogleDrive app on his iPhone.

    1. Hello, Mr Google I'm GoogleDrive app, could I get access to Google Drive services? Here is my client id and the URL you can call me back on.
    2. Which account, please identify yourself?
    3. I am Bob, here's my login and password
    4. Hello, Bob, do you want to grant access to GoogleDrive app?
    5. Yes I do. I trust this app.
    6. Ok fine, Let me use the client callback URL to get back to the app. Here is an access code for you GoogleDrive.
    7. Thanks. Mr Google may I exchange my code for an access token?
    8. Here's your access token GoogleDrive Enjoy.
    9. So Mr Google could I get access to Bob's list of files, here's my access token.
    10. Let me see if your access token is still valid, ok fine here's the list your requested.

    Easy peasy.
    One of the reasons of OAuth2 (apart from the cryptographic signatures vs SSL/TLS protection) is the need to serve not only web app but also, mobile app. OAuth2 defines 3 type of profiles: web application (end user has no access to credentials, access tokens), web browser client (OAuth credential not trusted, some provider won't issue a client_secret) and native application (dynamically issued credentials such as access tokens or refresh tokens can receive an acceptable level of protection).

    For native app one of the main challenge is somehow to include a third party UI to grant access either by forwarding your app to a web browser or by embedding a WebView and, once successfully authenticated and authorized, to go back to the original app. With minimal provider configuration, AeroGear libraries handles it for you.

    In the second part of this blog, we'll see how using AeroGear iOS libraries, we managed the OAuth2 back and forth discussion easily and how the OAuth2 implementation integrates transparently with Pipes.