Showing posts with label Phone Gap. Show all posts
Showing posts with label Phone Gap. Show all posts

Saturday, July 25, 2015

OAuth with Meetup and PhoneGap

Why?

Many sites like Facebook, Google, Meetup etc have APIs to allow third party applications perform actions on behalf of users.This integration provides powerful features and better user experience. Imagine ability to share a thought originally posted in your application to be shared on Facebook and Twitter automatically. Or comment on a meetup event from your mobile app.

OAuth allows third party apps securely access website or application on behalf of the user. User doesn't share user id password to third party application. Rather will authenticate with the original application. User will be prompted for set of features to authorize. If user authorizes successfully, the third party application could perform actions on behalf of the user.

 What?

In this blog, let's take a mobile app developed using Phone Gap authenticating with Meetup site. If user authenticates and authorizes the app, will access secure Meetup API on behalf of the user. Let's use C# for server side code. It could be a Web Site deployed on Cloud like Azure or a server within your premises.

In this blog we follow Server workflow where app authenticates with the website once. Unless user goes to Meetup site and resets access, app can continue to use the access provided. App is expected to securely store tokens on the server.

Note: 

Meetup OAuth allows user with an implicit flow where a third party app can authenticate user with Meetup site and use the resultant code to perform actions on behalf of the user. This is simple to use and App is not expected to store anything. The code will expire after a limited period of time. After that application need to authenticate again. Assuming the application is a web app in a browser, user is already logged into Meetup site on the machine, he/she won't see log-in prompt again. If not the user will get the login repeated.

This blog doesn't use this workflow, rather uses a more elaborate server workflow that doesn't involve user again as much as possible.


Following depicts server flow
Let's go through above steps 

Get access to use Meetup API (one-time)

To present Meetup login screen launch following Meetup URL. As mentioned above with OAuth user directly authenticates with the original site. No credentials are provided to the third party app.

'https://secure.meetup.com/oauth2/authorize?client_id=YOUR Key&response_type=code&redirect_uri=http://localhost/my_app'

App needs to register with Meetup at this link. Client Id in above URL is a key. This is how meetup identifies the third party apps.

In the same page you need to specify a redirect Url, once authenticated successfully Meetup redirects to this Url. In the example it's  http://localhost/my_app.  When redirected Meetup responds back with User Token in query string. 

In Phone Gap this could be achieved using InAppBrowser plugin. Install In App Browser with the following command

cordova plugin add org.apache.cordova.inappbrowser

In the Phone Gap app launch a new window (that uses InAppBrowser) to present Meetup login screen to the user.

var windowReferece = window.open('https://secure.meetup.com/oauth2/authorize?client_id=YOUR_Key&response_type=code&redirect_uri=http://localhost/my_app', '_blank', 'location=yes')

// As callback Url starts to load, handle the returned Url to get user token
windowReferece.addEventListener("loadstart",function(event){
if(event && event.url){
var replyUrl = event.url;

// extract token out of the Url
var token = replyUrl.substring(replyUrl.indexOf("code=")+5);
if(token.indexOf("&") >= 0){
token = token.substring(0,token.indexOf("&"));
}
                console.log(token);
}
});

Send given token to your API server, that handles rest of authentication and makes secure Meetup API calls. In this sample, I'm using C# server side code to further authenticate and get access tokens for secure meetup API calls.

Consider following code to get Access Token using User Token generated in the Phone Gap app,

            #region Create Request to get access token

// Client Id and secret are generated while registering with Meetup earlier at this link

                var requestContent =
    "client_id=Your client id&" + // 
    "client_secret=your client secret&" +
    "grant_type=authorization_code&" +
    "redirect_uri=http://localhost/my_app&" +
    "code=" + [[ user key obtained from Phone Gap app ]];
            WebRequest request = null;
            try
            {
                // Meetup OAuth API that gets access token
                request = WebRequest.Create("https://secure.meetup.com/oauth2/access");
                
                var requestBytes = Encoding.UTF8.GetBytes(requestContent);
                request.ContentType = "application/x-www-form-urlencoded";
                request.Method = "POST";
                request.ContentLength = requestBytes.Length;
                var requestStream = request.GetRequestStream();
                requestStream.Write(requestBytes, 0, requestBytes.Length);
                requestStream.Close();
            }
            catch (Exception exception)
            {
                Logger.LogError(exception);
                return string.Empty;
            }

            #endregion Create Request to get access token

            #region Make calls and handle response

            try
            {
                var response = request.GetResponse();
                var statusCode = ((HttpWebResponse)response).StatusCode; 

                 // Add checks based on status code.

                var responseStream = new StreamReader(response.GetResponseStream());

               // Response includes Access Token and Refresh Token. Store them (may be in DB) for Meetup API calls to use

                return responseStream.ReadToEnd();

            }
            catch (Exception exception)
            {
                Logger.LogError("Error while reading response obtained from OAUTH call. ", exception);
                return string.Empty;
            }

            #endregion Make calls and handle response

Make Secure Meetup API calls 

Above call returns Access Token, which could be used with any Meetup API that needs authentication. Consider following code

request = WebRequest.Create("https://api.meetup.com/2/member/self/");

// OAuth Access Token is passed along in request headers
                request.Headers.Add("Authorization", "Bearer " + ACCESS_TOKEN_OBTAINED_ABOVE);
                request.Method = "GET";
                
                var response = request.GetResponse();
                var statusCode = ((HttpWebResponse)response).StatusCode; 

                var responseStream = new StreamReader(response.GetResponseStream());
                var data = responseStream.ReadToEnd();

Remember, this access token is valid for 60 minutes only. After that request for access token again. Does that mean user need to be involved again? No. Use refresh token obtained in the first server side call to request for access token now on.

Following will be the request content to Meetup OAuth URL with refresh token

                var requestContent = "client_id=your client id&" +
                                     "client_secret=your client secret&" +
                                     "grant_type=refresh_token&" +
                                     "refresh_token=" + refreshToken;

That's it. Let's build some cool apps that integrate with Meetup.

For further reading and reference:
Meetup OAuth documentation
Phone Gap - Getting Started
What's Phone Gap - A basic explanation
Another way to do it - PhoneGap Plugin for OAuth
AngularJS Way - ng-cordova-oauth

Sunday, January 18, 2015

Push Notifications for Phone Gap App using GCM and Mobile Service

Push Notifications are part of our lives everyday. We get alerted about new emails, news updates, What's app, Facebook Twitter messages so on every hour (or every minute for some :D ). When an app is running, it could request for data and present it on the screen.Push Notifications alert user with information even when the app is NOT open. For example email alert appearing even when the email app is not running on the mobile device.

Windows Azure Mobile Services (integrated with Notification Hub) simplifies sending notifications to multiple mobile platforms, Android, iOS, Windows Phone 8 etc. In this blog, I'm writing about Android Notifications. Will be writing about other mobile platforms (using mobile services) in upcoming blogs.

Here is the agenda for this blog,
  1. GCM (Google Cloud Messaging)
  2. Azure Mobile Services for sending Push Notifications
  3. Phone Gap App registering and receiving notifications

GCM for Android:

For an app installed on your Android device, GCM can send messages over the internet even when the app is not open. It has the ability to queue messages when the device is offline and send it when connected (if message doesn't expire). These messages popup in the notification area. App can be launched when user taps on a notification.

Refer to the image depicting steps involved in sending push notification using GCM. This is one of the ways to use GCM. For the case I'm describing in the blog, with Azure Mobile service, this design fits.

In the five step process described in the image, i) each phone registering with GCM for notification will get a reg id. App will store Sender Id/Project number and send it in the request to get reg id. ii) Developer need to generate API key through Google Developer Console and use it while sending Push Notifications. This way GCM is sure sender of the message is owner of the app.

Azure Mobile Service

Azure Notification Hub allows sending Push Notification to cross platform devices and apps. Mobile Service is integrated with Notification Hub allowing easy notifications. It acts as the server in above architecture. It automates most part of action 4 i) Saving reg Ids, ii) Storing API key and verifying with GCM and iii) Provides easy API for sending notifications. It also provides ready made client libraries for making GCM calls and registration with notification hub.

Let's get the app ready!

  1. Setup in Google Developer Console.
  2. Setup Mobile Services 
  3. Add Push Notifications to Hybrid Mobile App.

Setup in Google Developer Console.

  1. Log into Google Developer Console http://console.developers.google.com
  2. Click Create New Project, provide Project Name and click Create.
  3. Make a note of Project Number, that is the Sender Id.
  4. Click on the project to get into details view.
  5. In the left navigation panel, select Credentials under API & Auth.
  6. Click Create New Key. In the popup window, select server key.
  7. Make note of generated API Key.
  8. Click on APIs under APIs and Auth and make sure Google Cloud Messaging for Android is "ON"

Setup Mobile Service:

Log into Windows Azure Management Portal.
Enable Push Notifications on Mobile Services for GCM.
  1. Select the Mobile Service providing Push Notification functionality.
  2. Click on Push tab.
  3. Under Google Cloud Messaging Settings, specify API Key noted from Google Developer Console.
Update script that triggers Push Notification
  1. Select Data tab and click on table that need to trigger Push Notification.
  2. Click on Script tab.
  3. Select appropriate action among Insert, Update, Delete or Read. And update following code. (read descriptions in the comments)
// add additional behavior to request.execute for Push Notification.
request.execute({
       success: function() {
           // template for the payload
           var pn= '{ "message" : "Sample push notification" }';
// push.send notifies all configured mobile platforms (iOS, Android, WP8) etc. Using push.gcm.send for GCM only.
// first parameter of send specifies all tags. 

           push.gcm.send(null, pn, {
               success: function(response){
                   console.log("Sent push:", response);
                   request.respond();
               },
               error: function (error) {
                   console.log("Failed to send PN", error);
                   request.respond(500, { error: pushResponse });
               }
            });
       }
   });

Enable Phone Gap mobile app to send Push Notifications:

Add push plugin-
cordova plugin add https://github.com/phonegap-build/PushPlugin.git

Device plugin
cordova plugins add https://github.com/apache/cordova-plugin-device.git

Note: include scripts for Mobile Service and Notifications Hub MobileServices.Web-1.2.5.js and NotificationHub.js. They are downloadable from Mobile Service HTML/JS template project.
// set up Push Notification as the app is launched and deviceReady event is fired

 var pushNotification = window.plugins.pushNotification;

// Mobile Client object is created at script file level.
 mobileClient = new WindowsAzure .MobileServiceClient("https://my.mobile.service.url/", "my-mobile-service-key");

  if(pushNotification){
// Phone Gap App and Mobile Service could be used for non Android platforms as well. So check this condition. Else conditions on other mobile platforms shall be added.
if ( device.platform == 'android' || device.platform == 'Android' ){
       pushNotification.register(
           app.successHandler, app.errorHandler,
               "senderID": GCM_SENDER_ID, // it's the project number
               "ecb": "gcmCallback" // function to be called by GCM Service for completing registration.
              });
            } 
         }


GCM Callback below.
    function gcmCallback (e) {
     switch (e.event) {
//This function is called in two scenarios, while registering device with GCM and while receiving Push Notifications
     case 'registered':
          if (e.regid.length > 0) { // regId will be sent back to Notification Hub/ Mobile Service.
              if (mobileClient) {
// Client library for notification hub -that allows multi-platform Push Notifications. Of course, Here we are focusing on GCM/Android.
             var notificationHub= new NotificationHub(mobileClient);

 // Set template for data to be sent by Mobile Service
            var template = "{ \"rootObject\" :{\"message\":\"$(message)\"}}";

// Send regId to Notification Hub, so that mobile service uses it when sending Push Message.
             notificationHub.gcm.register(e.regid, null, "any-template-name", template).done(function () {
// This is my function for updating status on view. You can use console.log or any other method.
              app.updateStatus("Registered for new notifications, through Notification Hub");
                        }).fail(function (error) {
               app.updateStatus("error);
                        });
                    }
                }
                break;
// This is handling Push Message
            case 'message':
            // if app is open when notification arrives
                if (e.foreground)
// Perform target action when notification arrives. Again, this my function update view. You can handle it diferently.
                    app.updateStatus(e.payload.message);

                }
                break;

            case 'error':
                app.updateStatus('GCM error: ' + e.message);
                break;

            default:
                app.updateStatus('An unknown GCM event has occurred');
                break;
        }
    }