The core of the Frog Development Platform is the HTTP API. FDP "widgets" are a convenient javascript environment in which to communicate with this API and present something to the user, but sometimes they are not powerful enough to achieve what you want. If you find your application needs local structured storage, you want to write it in a particular language (i.e. PHP, Ruby, Python rather than javascript), or you cannot achieve the security you need in client side programming, you will need to consider writing your own application outside the FDP widget environment.
If you write an application outside the FDP widget environment, you still need to communicate with frog data and API. You can do this using a standard protocol called oAuth. The job of "oAuth" is to safely allow an external application to authenticate against and communicate with your Frog server.
Lets say we want to build an online assessment for our students; it requires features that make it impossible to build as a normal Frog page or as an FDP widget (our easiest options). So, instead we choose to build a separate application in our language of choice (lets say PHP). Students using this application need to authenticate using the Frog server, and the application needs to communicate with the Frog server. So our application needs to talk to Frog.
In oAuth speak, the Frog server is a Service Provider. It holds the master user details and authoriative version of data. Our application itself is called a Consumer. The user interacts with our application, which in turn interacts with the Frog server.
To use oAuth your application will need to speak directly to the Frogbox (the communication is direct rather than routed through the client's browser. Therefore where you host your application is important, it needs to be able to 'see' the Frog server. For example, if you host your application externally, your Frog server needs to be visible externally (not hidden behind a VPN).
Not just anyone can connect to your Frog server; you need to explicitally whitelist applications ("consumers") in the toolkit. To do this go into the toolbox, click on the “FDP” tab and then “oAuth”. Create a new consumer by filling in a name and then clicking 'Add'.
Your application will need to know its public and private key. If you click the 'show auth' link next to your new application these keys will be displayed. You will need to store this public key and secret key somewhere where you own application can access them.
It is important to realise that the oAuth model leaves full control in the hands of the Frog server (and therefore Frog administrator). In this admin interface you can enable and disable the applications that can connect to the Frogbox at any time with immediate effect. You will notice that you already have some standard consumers (such as one for our Outlook Plugin product) that we have already created on your server. Most of these you can enable and disable yourself.
A brief overview of the oAuth process required for our web application would be:
As you can see, it's quite a merry dance! There are many good online tutorials about the technical details of oAuth 1.0 implementation, so I will point you in that direction rather than reproduce another set here; all the information you need about the Frog implementation is detailed in the next section.
Frog is a standard implemention of the oAuth 1.0a protocol, and all requests must be signed with the HMAC-SHA1 algorithm.
| Endpoint | URL |
|---|---|
| request token | http://frogserver.com/api/1/oauth1.php/request-token |
| authorise | http://frogserver.com/app/login |
| access token | http://frogserver.com/api/1/oauth1.php/access-token |
If you are implmentating oAuth connections to your Frog server on a desktop application (e.g. an AIR or iphone app) which does not have a URL, Frog's oAuth implementation supports an "Out of Bounds" mode. When getting a request token provide the string 'oob' as the callback URL. Once the user has authenticated on the frogbox, they will be given a code to enter in your application, your application can then use this as the oAuth verification code to exchange the request token for an access token.
The HTTP API endpoint of http://frogserver.com/api/1/ is oAuth aware so you can make any requests to the existing APIs documented on this site. A particularly useful method straight after you have gained the access-token is http://frogserver.com/api/1/?method=auth.whoami.
The HTTP API supports the standard oauth parameters and signature being passed via either:
The header method is recommended.
An important caveat applies at this point: the platform supports oAuth connectivity but when you connect through a consumer that connection assumes all the roles of the person that has authenticated. i.e. unlike FDP widgets an oAuth consumer cannot be restricted to just a sub-section of roles and the actions they can perform are anything that they could do within the platform if they logged in directly (theoretically, obviously at this point they are restricted by what is actually in the API). Restricting consumers to a sub-set of roles is something we are working towards, but is not here yet.
We have an example hacked together PHP application that can be used as a learning tool run locally, or members of the community had added various solutions in the comments of this post.
This tutorial started life as a question from David Welch about when the oAuth documentation would be released, but it built up a useful comment trail so turned it into an actual tutorial itself (thus some of the answers being addressed to David!)
Hi David --
Yup, you're right our documentation is currently catching up with our actual release. We are writing a proper tutorial and will be releasing a demo application (an inter-school quiz) fairly shortly. I understand your enthusiasm to get stuck in, this is a great addition to our platform so in this reply I will include very briefly the absolute essentials that you need to start using it.
To use oAuth your school will need to have a publically accessible URL (that's not hidden behind a VPN).
Your oAuth client needs to know 3 URLs:
The HTTP API endpoint is http://frogserver.com/api/1/ is oAuth aware so you can make any requests to the existing APIm documented on this site, a particularly useful method straight after you have gained the access-token is http://frogserver.com/api/1/?method=auth.whoami (also not yet documented).
The HTTP api documented on this site is demo-ed used within FDP widgets, to use over oAuth you need to pass in the parameters as either GET or POST params (GET for retrieval, POST for an action that changes data i.e. mostly GET). The only "special" parameter is 'method', the other params are as documented. i.e. to get the user details for user id 23 and 56
http://frogserver.com/api/1/?method=user.getinfo&id=23,56
Obviously in all the above frogserver.com is your own school public URL. Any API request authenticated by oAuth will need to have a bunch more params but these are the standard params as defined by the oAuth spec.
The oAuth 1.0a specification is supported. Requests must be signed with the HMAC-SHA1 algorithm, and the authorisation can be sent in the Authorization header (preferred), POSt or GET parameters. Out-of-bounds (OOB) applications (i.e. non-web applications) are supported if the request token callback string is set to 'oob'.
To start using oAuth you must register your application on the Frogbox you wish to oAuth against. To do this go into the toolbox, click on the “FDP” tab and then “oAuth”. Create an application and then you will need to store your consumer key and secret somewhere where you application can access them.
Phew! You got all that :-P?
Some links to get you started if you're new to oAuth...
oAuth is a standard protocol so these documents and implmentation should apply, just swap out the URL endpoints for the endpoints of your own frogbox as specified above.
An important caveat applies at this point: this release brings oAuth connectivity to the platform but when you connect through a consumer that connection assumes all the roles of the person that has authenticated. i.e. unlike FDP widgets an oAuth consumer cannot be restricted to just a sub-section of roles and the actions they can perform are anything that they could do within the platform if they logged in directly (theoretically, obviously at this point they are restricted by what is actually in the API). Restricting consumers to a sub-set of roles is something we are working towards, but is not here yet.
Does that help? I think that covers the basics but we'll be releasing something a bit more friendly soon to help get you started. Any particular questions, just fire away and we'll do our best to explain.
In the spirit of sharing, below is the source for an Abobe Air oAuth pin implementation with a few calls to get the users details. It uses the https://github.com/bytespider/jsOAuth script for the heavy lifting. You can find the complete app @ http://dl.dropbox.com/u/236573/source.zip.
$(document).ready(function(){
var user, config;
var api_url = '';
config = {
consumerKey: '',
consumerSecret: '',
requestTokenUrl: api_url + '/api/1/oauth1.php/request-token',
authorizationUrl: api_url + '/app/login',
accessTokenUrl: api_url + '/api/1/oauth1.php/access-token'
};
var oauth = new OAuth(config);
oauth.fetchRequestToken(auth_window, error_handler);
function auth_window(url){
window.open(url, 'authorise', 'height=400, width=500');
}
function error_handler(data){
air.Introspector.Console.log(data);
}
function populate_user_data(id){
oauth.get(api_url + "/api/1/?method=auth.whoami",
function (response_whoami) {
response_whoami = jQuery.parseJSON(response_whoami.text);
oauth.get(api_url + "/api/1/?method=users.getinfo&id=" + response_whoami.data.id,
function (response_getinfo){
response_getinfo = jQuery.parseJSON(response_getinfo.text);
user = response_getinfo.data[0];
$('body').append('<div><p>hi ' + user.firstname + '. I\'ve got the user data on ya..</p></div><table><tr><td>ID</td><td>' + user.id + '</td></tr><tr><td>UUID</td><td>' + user.uuid + '</td></tr><tr><td>Name</td><td>' + user.firstname + ' ' + user.surname + '</td></tr></table>');
$('#authorise-pin').fadeOut();
},
error_handler
);
},
error_handler
);
}
$('#authorise-pin').submit(function(){
pin = $('#pin').val();
oauth.setVerifier(pin);
oauth.fetchAccessToken(populate_user_data, error_handler);
return false;
});
});
Here is my solution in Java for Android 2.2. It uses the Signpost OAuth library. I have cut and pasted the bits out of the class that are the most important.
private static OAuthConsumer consumer;
private static OAuthProvider provider;
//Acquire a request token
public void Authorize() throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException
{
//Initialise global OAuthConsumer and OAuthProvider here
consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
provider = new DefaultOAuthProvider(REQUEST_TOKEN_RESOURCE, ACCESS_TOKEN_RESOURCE, AUTHORIZE_URL);
//Step 1 Request token from frog server
String authurl = provider.retrieveRequestToken(consumer,OAuth.OUT_OF_BAND);
//load the auth url in the webview
webview.loadUrl(authurl);
This bit is a simple authorize method which sets up the consumer and provider variables. It gets the request token from the request token url via the provider passing the key and secret from within consumer along with an OOB parameter. It then prompts the webview to load the login and connect url (frog connect).
public void FetchPIN()
{
try
{
webview.getSettings().setJavaScriptEnabled(true);
webview.addJavascriptInterface(new JSInterface(), "HTMLOut");
webview.loadUrl("javascript:window:HTMLOut.showHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
String html = FetchedHTML;
if (html != "")
{
String divstart = "<p><strong>Your authorisation code is ";
if (html.contains(divstart))
{
//found authentication page
int lastIndexOfStartString = html.lastIndexOf(divstart);
String[] splits = html.split("\n");
String code = "";
for (int i = 0; i < splits.length; i++)
{
//Log.i("HTMLLINE", splits[i]);
if (splits[i].contains(divstart))
{
String[] linesplits = splits[i].split(" ");
for (int a = 0; a < linesplits.length; a++)
{
if (linesplits[a].contains(","))
{
//Log.i("KEYLINE", linesplits[a]);
//found key code item
code = linesplits[a].substring(0, 6);
AUTH_PIN = code;
Log.i("RQ", AUTH_PIN);
i = splits.length;
a = linesplits.length;
//Next Step is to get AccessToken
provider.retrieveAccessToken(consumer, AUTH_PIN);
ACCESS_TOKEN = consumer.getToken();
ACCESS_SECRET = consumer.getTokenSecret();
//Query the server
String response = SendASignedRequest("http://frog.themfg.co.uk/api/1/?method=auth.whoami");
//close webview and show in from
}
}
}
}
}
}
}
catch (Exception e)
{
System.out.println("ERROR: " + e.getMessage());
}
}
This section parses the webview and pulls the html content out. It is a bit messy so apologies for that - but it parses through HTML looking for the "Your authorisation code is " bit and extracts the authorization pin code. Examples on the web doing this step usually say "Prompt the user to goto this url and enter the pin into the application". This does an extract so you're not worrying about the user getting the pin wrong - it is frog specific though.
It stores the AUTH_PIN globally, and the requests the access token and secret through the oauth library and stores those globally.
The last step calls to the "get something from the server" method, which is shown below:
public String SendASignedRequest(String requestString) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException
{
//ask server something
URL url = new URL(requestString);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
consumer.sign(request);
System.out.println("Response: " + request.getResponseCode());
System.out.println("Body: " + request.getResponseMessage());
return request.getResponseMessage();
}
This method takes the request string (what you want to pass to the frog api, and signs the request with the consumer (and the authentication done previously). It just prints out the response code (should be 200) and the response message.
This and the code in C# above should be a good enough backbone for any mobile applications for Android, J2ME and Win Phone 7. iPhone apps will probably require different libraries - haven't really looked into any Objective-C oauth applications... yet.
private static String CONSUMER_KEY = "";
private static String CONSUMER_SECRET = "";
private static String REQUEST_URL = "http://frogserver/api/1/oauth1.php/request-token";
private static String AUTH_END_POINT = "http://frogserver/app/login?oauth_token=";
private static String ACCESS_END_POINT = "http://frogserver/api/1/oauth1.php/access-token";
private static String REQUEST_TOKEN = "";
private static String ACCESS_SECRET = "";
private static String ACCESS_TOKEN = "";
private static String CALLBACK_URL = "FrogTimetable:///frog";
private OAuth.Manager oauth;
public Form1()
{
InitializeComponent();
Authorize();
}
public void Authorize()
{
//step 1 get request token
oauth = new OAuth.Manager();
oauth["consumer_key"] = CONSUMER_KEY;
oauth["consumer_secret"] = CONSUMER_SECRET;
oauth.AcquireRequestToken(REQUEST_URL, "POST");
var url = AUTH_END_POINT + oauth["token"];
webBrowser1.Url = new Uri(url);
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
//when website loaded
var divMarkerStart = "<p><strong>Your authorisation code is ";
var divMarkerEnd = ",";
String doc = webBrowser1.DocumentText;
String substr1 = doc.Substring(doc.IndexOf(divMarkerStart) + divMarkerStart.Length);
String substr2 = substr1.Substring(0, substr1.IndexOf(divMarkerEnd));
if (substr2.Length < 7)
{
REQUEST_TOKEN = substr2; //got request token
GetAccessToken();
}
}
catch (Exception ex)
{
//throw;
}
//webBrowser1.Dispose();
//start next stage
//Get Access Token
}
public void GetAccessToken()
{
oauth.AcquireAccessToken(ACCESS_END_POINT, "POST", REQUEST_TOKEN);
byte[] buf = new byte[1024];
//get user status or frog example
String teststr = "http://frogserver/api/1/?method=auth.whoami";
var authHeader = oauth.GenerateAuthzHeader(teststr, "GET");
var request = (HttpWebRequest)WebRequest.Create(teststr);
request.Method = "GET";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authHeader);
using (var response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode != HttpStatusCode.OK)
{
MessageBox.Show("There's been a problem trying to pull from frog: " + Environment.NewLine + response.StatusDescription);
}
StringBuilder sb = new StringBuilder();
System.IO.Stream stream = response.GetResponseStream();
string tempstr = "";
int cnt = 0;
do
{
cnt = stream.Read(buf, 0, buf.Length);
if (cnt != 0)
{
tempstr = Encoding.ASCII.GetString(buf, 0, cnt);
sb.Append(tempstr);
}
}
while (cnt > 0);
UserInfo ui = parseUserInfo(sb.ToString());
}
}`enter code here`
This is a solution in C#.NET (4.0 - although it doesn't differ from 2,3 or 3.5) for OAuth in Frog. It uses an open source C# Oauth wrapper - although the other free libraries knocking around the web will do the same sort of stuff.
I am going to port this to android over the week to see if I can get any joy with that.
Had a few responses of Bad Request (400) - mainly due to incorrect use of POST and GET so watch out for that.
Hope this helps anyone looking into doing OAuth and Frog,
Marc
Hi All
Many libraries generate nonce of 8 chars, the frog provider requires at least 10.
Great stuff, our docs are very slow aren't they sorry! Will raise internally. Note that doing POSTs to the api you need to be careful with the placement of the parameters -- our api expects ?method=users.getInfo&fmt=xml in the GET paramters always, and if you need to POST, the actual params to pass to the function in the POST portion. The two are treated differently in the way the oAuth signature is built.
Repeated, ignore this.
