hello.js

A client-side Javascript SDK for authenticating with OAuth2 (and OAuth1 with a oauth proxy) web services and querying their REST API's. HelloJS standardizes paths and responses to common API's like Google Data Services, Facebook Graph and Windows Live Connect. It's modular, so that list is growing. No more spaghetti code!

...I did not understand any of that

Fear not. Here is a tiny demo illustrating a basic request: Connect a user through a service they belong to, e.g.


Features

Here are some more demos...

Windows Facebook Google More..
Profile: name, picture (email)
Friends/Contacts: name, id (email)
Albums, name, id, web link
Photos in albums, names, links
Photo file: url, dimensions
Create a new album
Upload a photo
Delete an album
Status updates
Update Status

Install

Download: HelloJS | HelloJS (minified)

Compiled source, which combines all of the modules, can be obtained from Github, and source files can be found in Source.

Bower Package

# Install the package manager, bower
npm install bower

# Install hello
bower install hello

The Bower package shall install the aforementioned "/src" and "/dist" directories. The "/src" directory provides individual modules which can be packaged as desired.

Note: Some services require OAuth1 or server-side OAuth2 authorization. In such cases, HelloJS communicates with an OAuth Proxy.

Help & Support

Quick Start

Quick start shows you how to go from zero to loading in the name and picture of a user, like in the demo above.

  1. Register your app domain
  2. Include hello.js script
  3. Create the signin buttons
  4. Setup listener for login and retrieve user info.
  5. Initiate the client_ids and all listeners

1. Register

Register your application with at least one of the following networks. Ensure you register the correct domain as they can be quite picky.

2. Include Hello.js script in your page.

3. Create the signin buttons

Just add onclick events to call hello( network ).login(). Style your buttons as you like; I've used zocial css, but there are many other icon sets and fonts.

4. Add listeners for the user login

Let's define a simple function, which will load a user profile into the page after they sign in and on subsequent page refreshes. Below is our event listener which will listen for a change in the authentication event and make an API call for data.

5. Configure hello.js with your client_id's and initiate all listeners.

Now let's wire it up with our registration detail obtained in step 1. By passing a [key:value, ...] list into the hello.init function. e.g....

That's it. The code above actually powers the demo at the start so, no excuses.

Core Methods

hello.init()

Initiate the environment. And add the application credentials.

hello.init( {facebook: id, windows: id, google: id, .... } )

name type
credentials object( key => value, ...  )
name type example description argument default
key string windows, facebook or google App name"s required n/a
value string 0000000AB1234 ID of the service to connect to required n/a
options set's default options, as in hello.login()

Example:

hello.init({
	facebook : '359288236870',
	windows : '000000004403AD10'
});

hello.login()

If a network string is provided: A consent window to authenticate with that network will be initiated. Else if no network is provided a prompt to select one of the networks will open. A callback will be executed if the user authenticates and or cancels the authentication flow.

hello.login( [network] [,options] [, callback() ] )

name type example description argument default
network string windows, facebook One of our services. required null
options object
name type example description argument default
display string popup, page or none "popup" - as the name suggests, "page" - navigates the whole page, "none" - refresh the access_token in the background optional popup
scope string email, publish or photos Comma separated list of scopes optional null
redirect_uri string redirect.html A full or relative URI of a page which includes this script file hello.js optional window.location.href
response_type string token, code Implicit (token) or Explicit (code) Grant flow optional token
force Boolean true or false Always initiate auth flow, despite current valid token. optional true
callback function function(){alert("Logged in!");} A callback when the users session has been initiated optional null

Examples:

hello( "facebook" ).login().then( function(){
	alert("You are signed in to Facebook");
}, function( e ){
	alert("Signin error: " + e.error.message );
});

hello.logout()

Remove all sessions or individual sessions.

hello.logout( [network] [, options ] [, callback() ] )

name type example description argument default
network string windows, facebook One of our services. optional null
options object
name type example description argument default
force boolean true If set to true, the user will be logged out of the providers site as well as the local application. By default the user will still be signed into the providers site. optional false
callback function function(){alert("Logged in!");} A callback when the users session has been initiated optional null

Example:

hello( "facebook" ).logout().then( function(){
	alert("Signed out");
}, function(e){
	alert( "Signed out error:" + e.error.message );
});

hello.getAuthResponse()

Get the current status of the session. This is a synchronous request and does not validate any session cookies which may have expired.

hello.getAuthResponse( network );

name type example description argument default
network string windows, facebook One of our services. optional current

Examples:

var online = function(session){
	var current_time = (new Date()).getTime() / 1000;
	return session && session.access_token && session.expires > current_time;
};

var fb = hello( "facebook" ).getAuthResponse();
var wl = hello( "windows" ).getAuthResponse();

alert(( online(fb) ? "Signed":"Not signed") + " into FaceBook, " + ( online(wl) ? "Signed":"Not signed")+" into Windows Live");

hello.api()

Make calls to the API for getting and posting data.

hello.api( [ path ], [ method ], [ data ], [ callback(json) ] )

name type example description argument default
path string /me, /me/friends Path or URI of the resource. See REST API, Can be prefixed with the name of the service. required null
method get, post, delete, put See type HTTP request method to use. optional get
data object {name:Hello, description:Fandelicious} A JSON object of data, FormData, HTMLInputElement, HTMLFormElment to be sent along with a get, postor putrequest optional null
callback function function(json){console.log(json);} A function to call with the body of the response returned in the first parameter as an object, else boolean false. optional null

Examples:

hello( "facebook" ).api("me").then(function(json){
	alert("Your name is "+ json.name);
}, function(e){
	alert("Whoops! " + e.error.message );
});

Event subscription

hello.on()

Bind a callback to an event. An event may be triggered by a change in user state or a change in some detail.

hello.on( event, callback );

event description
auth Triggered whenever session changes
auth.login Triggered whenever a user logs in
auth.logout Triggered whenever a user logs out
auth.update Triggered whenever a users credentials change

Example:

var sessionstart =  function(){
	alert("Session has started");
};
hello.on("auth.login",sessionstart);

hello.off()

Remove a callback. Both event name and function must exist.

hello.off( event, callback );

hello.off("auth.login",sessionstart);

Misc

Pagination, Limit and Next Page

A convenient function to get the `next` result set is provided in the second parameter of any GET callback. Calling this function recreates the request with the original parameters and event listeners. Albeit the original path is augmented with the parameters defined in the paging.next property.

hello( "facebook" ).api( "me/friends", {limit: 1} ).on( 'success', function( json, next ){
	if( next ){
		if( confirm( "Got friend "+ json.data[0].name + ". Get another?" ) ){
			next();
		}
	}
	else{
		alert( "Got friend "+ json.data[0].name + ". That's it!" );
	}
}).on('error', function(){
	alert("Whoops!");
});

Scope

The scope property defines which privileges an app requires from a network provider. The scope can be defined globally for a session through hello.init(object, {scope:'string'}), or at the point of triggering the auth flow e.g. hello('network').login({scope:'string'});

An app can specify multiple scopes, separated by commas - as in the example below.

hello( "facebook" ).login( {scope: "friends,photos,publish" } );

Scopes are tightly coupled with API requests, which will break if the session scope is missing or invalid. The best way to see this is next to the API paths in the hello.api reference table.

The table below illustrates some of the default scopes HelloJS exposes. Additional scopes may be added which are proprietary to a service, but be careful not to mix proprietary scopes with other services which don't know how to handle them.

Scope Description
default Read basic profile
friends Read friends profiles
photos Read users albums and photos
files Read users files
publish Publish status updates
publish_files Publish photos and files

It's good practice to limit the use of scopes and also to make users aware of why your app needs certain privileges. Try to update the permissions as a user delves further into your app. For example: If the user would like to share a link with a friend, include a button that the user has to click to trigger the hello.login with the 'friends' scope, and then the handler triggers the API call after authorization.

Error handling

Errors can be returned in listeners to 'error' event, i.e. hello.api([path]).on('error', [errorhandler]) or the 'complete' event, hello.api([path]).on('complete', [completehandler]) - which may also be written as hello.api([path], [completehandler]).

The Promise response standardizes the binding of errorHandlers.

Error Object

The first parameter of a failed request to the errorHandler may be either boolean (false) or be an Error Object...

name type
error object
name type example description argument default
code string request_token_unauthorized Code required n/a
message string The provided access token.... Error message required n/a

Extending the services

Services are added to HelloJS as "modules" for more information about creating your own modules and examples, go to Modules

OAuth Proxy

A list of the service providers OAuth* mechanisms is available at Provider OAuth Mechanisms

For providers which support only OAuth1 or OAuth2 with Explicit Grant, the authentication flow needs to be signed with a secret key that may not be exposed in the browser. HelloJS gets round this problem by the use of an intermediary webservice defined by oauth_proxy. This service looks up the secret from a database and performs the handshake required to provision an access_token. In the case of OAuth1, the webservice also signs subsequent API requests.

Quick start: Register your client_id + client_secret at the OAuth Proxy service, Register your App

The default proxy service is https://auth-server.herokuapp.com/. Developers may add their own network registration AppID/client_id and secret to this service in order to get up and running.

Alternatively recreate this service with node-oauth-shim. Then override the default oauth_proxy in HelloJS client script in hello.init, like so...

hello.init(
	CLIENT_IDS,
	{
		oauth_proxy : 'https://auth-server.herokuapp.com/proxy'
	}
)

Enforce Explicit Grant

Enforcing the OAuth2 Explicit Grant is done by setting response_type=code in hello.login options - or globally in hello.init options. E.g...

hello( network ).login({
	response_type : 'code'
});

Refresh Access Token

Access tokens provided by services are generally short lived - typically 1 hour. Some providers allow for the token to be refreshed in the background after expiry.

A list of services which enable silent authentication after the Implicit Grant signin Refresh access_token

Unlike Implicit grant; Explicit grant may return the refresh_token. HelloJS honors the OAuth2 refresh_token, and will also request a new access_token once it has expired.

Bullet proof requests

A good way to design your app is to trigger requests through a user action, you can then test for a valid access token prior to making the api request with a potentially expired token.

var google = hello('google');
// Set force to false, to avoid triggering the OAuth flow if there is an unexpired access_token available.
google.login({force:false}).then(function(){
	google.api('me').then(handler);
});;

Promises A+

The response from the async methods hello.login, hello.logout and hello.api return a thenable method which is Promise A+ compatible.

For a demo, or, if you're bundling up the library from `src/*` files, then please checkout Promises

Browser Support

Browser
IE10
IE9
IE8
IE7
FF
CR
SA
OP
Mob
Mini5
iOS WP 7
hello.js 1,2 3 4
  1. IE7: Makes beeping sounds whenever the POST, PUT or DELETE methods are used - because of the XD, IFrame+Form+hack.
  2. IE7: Requires JSON.js and localStorage shims.
  3. Opera Mini: Supports inline consent only, i.e. reloads original page.
  4. WP7: Supports inline consent only, i.e. reloads original page.

Phonegap Support

HelloJS can also be run on phonegap applications. Checkout the demo hellojs-phonegap-demo

Contributing

"No, It's perfect!".... If you believe that then give it a star.

Having read this far you have already invested your time, why not contribute!?

HelloJS is constantly evolving, as are the services which it connects too. So if you think something could be said better, find something buggy or missing from either the code, documentation or demos then please put it in, no matter how trivial.

Changing code?

Ensure you setup and test your code on a variety of browsers.

# Using NodeJS on your dev environment
# cd into the project root and install dev dependencies 
npm install -l

# run continuous integration tests
grunt test