Welcome to the second post in a series showing you how to use the OAuth2 authentication mechanism in HANA XS. The mechanism enables you to use APIs from providers like Google, Facebook, Twitter or others offering this this authentication. Of course you will also learn how to authenticate against the HANA Cloud Platform and an ABAP server.
In this particular post we are going to implement step by step an end to end XS application calling a Google API.
Currently the series contains following posts:
In order to get the whole scenario running I am using a productive HANA cloud instance with revision 91. The instance can be reached from Google servers. This is a necessary prerequisite for Google’s web server application scenario. It is also possible to use OAuth2 with an on premise HANA box, which cannot be reached from the Internet.
We are going to perform the following steps:
So let’s start!
In order to make Google API calls later on, we first of all have to register our client application at Google’s developer console. During the registration we receive credentials, we later use to register instances of our application. This means every user has to grant access to his own data - nevertheless all application instances share the same basic client ID and secret.
This is achieved by:
Your Google client configuration should now look like this:
Later on we need the client ID and secret. The other information is already deployed in your HANA XS box for Google as an authorization provider. The developer console provides a convenient ‘Download JSON’ button to save this information to your local machine.
Another thing we can do right now is the enablement of the API we want to call later on. In this post we will use the Google+ API and this is the way to enable it:
If you want to configure OAuth authentication in XS, several configuration aspects come into play. There are well-known ones as XS HTTP destinations and XS Trust Stores, but also some new ones, namely XS OAuth configuration packages. We are going to create everything from scratch in a package called googleAPI. The only thing being reused is the configuration for calling the Google API (more on this later).
To perform all the tasks your user needs to have several roles:
Google only allows secure calls to their APIs (via HTTPS). So we have to set up a XS HTTP destination using a Trust Store. This Trust Store holds the server certificate – signed by a certification authority. We have to retrieve this certificate and import it into a trust store.
(I am using Chrome under Windows 7 in this example)
We want to have a folder containing everything we configure and are going to do so via the web IDE:
For reasons of demonstration we make things as simple as possible and switch off authentication:
FROM
"authentication": [{
"method": "Form"
}]
TO
"authentication": null,
it looks like
We already created the trust store and imported Google's certificate. Now we link this trust store to the HTTP destination so we retrieve a HTTPS destination.
Using the Web IDE, we do this by creating a new file in our package called
googleAPIdestination.xshttpdest
with following content:
host = "www.googleapis.com";
port = 443;
pathPrefix = "";
proxyType = none;
authType = none;
useSSL = true;
timeout = 0;
To connect the http destination to the trust store, we go to the XS Admin again.
OAuth is an open standard. Due to the fact that it has been in a draft version for quite some time, different providers implemented slightly different solutions. They all are standard compliant, but use e.g. different names for parameters and the like.
HANA XS provides a mechanism to meet the requirements of this fact by providing a basic framework and provider specific configurations. These configurations consist of:
here you find: client ID, authentication type, endpoints, …
here you find: protocol steps, parameter names and http request location (header, body, …)
To link the metadata (config & flavor) to an actual XS application there is a final piece in the puzzle - completing an XS OAuth configuration package:
Links metadata to runtime data
SAP delivers templates for some providers (at the time of writing Google, HANA Cloud platform, SAP Jam, SAP ABAP). Of course it is possible to create further OAuth client configurations and I strongly would like to encourage to do so.
Use the Web IDE and create a file with the suffix .xsoauthappconfig in our application package. Details will be configured via the XS admin.
Let’s name the file googleOAconfig.xsoauthappconfig and give it the minimum content required:
{
"clientConfig" : "sap.hana.xs.oAuth.lib.providerconfig.providermodel:googleV1"
}
Now our XS application is linked to the SAP delivered Google provider model.
To provide the missing content of the OAuth configuration we switch over to the HANA XS Admin again:
Now this particular HTTP destination will use the OAuth authentication. We still are using the pre delivered default Google configuration, which is not aware of our client ID and secret (which we set up at Google's developer console). Even though it is possible, it is not a good idea to provide this information in the default configuration, as an upgrade of the HANA revision will overwrite our details. This is where the extension mechanism comes in handy:
As we want to call the Google Plus API later on, we need to tell the app configuration which scopes (authorizations) we require. To add the scope
That’s it for the configuration part – a no brainer, wasn’t it :wink: ?
Let’s go ahead and use the configured HTTP destination in a xsjs.
HANA XS SPS9 comes with an OAuth client library. It is a hidden a little bid… but maybe you want to have a look into package: sap.hana.xs.oAuth.lib and particularly at file oAuthClient.xsjslib ?
You will find useful HANA XS OAuth client for the OAuth scenario over there. I’d suggest you briefly look at the following table and use it as a reference later on so we can use the remainder of this post to finish our end to end scenario by an implementation.
// Example on how to load and initialize the library:
var oAuthClientLib = $.import("sap.hana.xs.oAuth.lib", "oAuthClient");
// The constructor requires package name and file name of configured HTTP destination
var oaC = new oAuthClientLib.OAuthClient (<folder>, <destfilename>);
Following methods are available:
Step description | Method | Comment | |
---|---|---|---|
Check for valid access token | boolean oaC.hasValidAccessToken() |
| |
Check for valid refresh token | boolean oaC.hasValidRefreshToken() | returns true if refresh token is available and the scope set of the application configuration is included in the set of the granted scopes | |
Get URI for user authorization | String oaC.userAuthorizeUri(<redirectURL>); | authCode flow only. Returns URI for user authorization on oauth sever. Such call causes a creation of State-cookie which is later used within GrantAccess Step. Parameter redirectURL is required as an end target after acquiring of access token. | |
Get Token(s) from Code | int oaC.userGrantAccess(code) | authCode flow only. Exchange the authorization code into a token set. This call is usually executed by a generic redirect implemented at https:/<server>:<port>/sap/hana/xs/oAuth/lib/runtime/tokenRequest.xsjs. | |
Get Accestoken from Refresh token | int oaC.userRefreshAccess() | authCode flow only. Uses the refresh to acquire a new Access token. Return a HTTP Status code of the used connection | |
Get Accesstoken from Saml Assertion | int oaC.samlAccess() | SAML Flow only. Exchange a SAML assertion into an token set | |
Data Processing | response oaC.processData(oHttpClient, request, googleDriveDest); | Implements a connection to the target server enriching the request with OAuth AccessToken. | |
Revokes Access Token | int oaC.userRevokeAccess() | Connects to the revocation end point and revokes available access and refresh tokens. Even if such connect fails tokens a removed from local database afterwords. HTTP return code of the executed call ( 200 Success etc.) |
Before we go into the implementation, remember we chose to use the Google Plus API by
Scopes define fine granular access permissions. Depending on the API you want to use, you need to use a particular scope. There are several ways to discover the perfect scopes for the task. I like using Google’s OAuth playground (https://developers.google.com/oauthplayground/ . This is also a very good resource to get a feeling for this API and the OAuth authorization flow itself. Other good resources are Google’s API explorer ( https://developers.google.com/apis-explorer/#p/ ) and of course the corresponding documentation https://developers.google.com/+/api/#auth.
Okay this was a lot of setup work. Stay tuned just a little longer before we finally implement and execute the application.
For this API, you need to get your google ID. To do so:
And finally: let's code!
Please enjoy the code – you really deserve it now:
var callGoogle = function() {
// you need to add yourGoogleID right here (get it via https://plus.google.com/ -> Profile -> copy the long number)
var yourGoogleID = "101287807282311627286";
var response;
// the HTTP client to send requests
var oHttpClient = new $.net.http.Client();
// load the library handling the OAuth protocol
var oAuthClientLib = $.import("sap.hana.xs.oAuth.lib", "oAuthClient");
// Google + API specific endpoints
var suffix = "/plus/v1/people/";
suffix += yourGoogleID;
// where we want to go
var request = new $.net.http.Request($.net.http.GET, suffix);
// initialize the HTTP destination and OAuth client
var myDestObj = $.net.http.readDestination("googleAPI", "googleAPIdestination");
var oaC = new oAuthClientLib.OAuthClient("googleAPI", "googleAPIdestination");
// SCOPES -> configure via XS Admin in the OAuth configuration package
//https://www.googleapis.com/auth/plus.me
// if you want to start from scratch, just set a breakpoint here and call this method
// oaC.userRevokeAccess();
// initialize the OAuth authorization code flow (and trace a debug message)
// do you know what happens if you call this URL via your Browser?
var url = oaC.userAuthorizeUri("https://sapgolf.hana.ondemand.com/googleAPI/callGooglePlusAPI.xsjs");
$.trace.debug("Auth URL is: " + url);
// if you called the URL via your browser paste the authorization code response into the 'code' variable (after uncommenting of course)
// var code;
// this is an alternative way to get the access tokens
// oaC.userGrantAccess(code);
// is our access token still valid, do we need to refresh it or didn't we receive anyone at all?
var validAT = oaC.hasValidAccessToken();
if (validAT) {
// call the API
response = oaC.processData(oHttpClient, request, myDestObj);
} else {
var validRT = oaC.hasValidRefreshToken();
if (validRT) {
var refreshStatus = oaC.userRefreshAccess();
if (refreshStatus === 200) {
// call the API
response = oaC.processData(oHttpClient, request, myDestObj);
}
} else {
$.response.setBody(JSON.stringify({
error: false,
errorDescription: "There are no tokens (access or refresh) for this application/user context available",
solution: "Authorize yourself via the following authorization URL",
authorizationURL: url
}));
return;
}
}
if (response) {
// display googles response
var myBody;
if (response.body)
try {
myBody = JSON.parse(response.body.asString());
} catch (e) {
myBody = response.body.asString();
}
$.response.contentType = "application/json";
$.response.status = 200;
$.response.setBody(JSON.stringify({
"status": response.status,
"body": myBody
}));
}
};
try {
callGoogle();
} catch (err) {
$.response.setBody("Failed to execute action: " + err.toString());
}
The first time you execute the application you will get something like:
The reason for this is the missing access token. We trigger the authorization flow by sending the required scopes, client id & secret and some more information to Google. Google sends us an authorization code back, which we finally exchange against access and refresh tokens. The access token will be stored in the OAuth secure store and used for upcoming API calls.
After you have approved the client request, you will see the well known Google OAuth log on UI
Log on to this one and grant access to the client
you will be redirected to the very same application you came from – this time showing you the JSON returned by the API call:
There are many configuration steps to carry out, before you can finally use OAuth authentication in you application. I hope you successfully completed all of them and most of all hope you see the potential you got at hand right now.
There will be some follow up posts dealing with the usage of the other pre delivered configuration packages, but also some going for even more advanced topics i.e. explaining how to create own configurations, how to debug outgoing requests and the like.
I would like to thank michael.engler and klaus.herter for their great support of this post!
I hope you all stay tuned and are looking forward the next posts.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
32 | |
24 | |
8 | |
7 | |
7 | |
6 | |
6 | |
6 | |
5 | |
4 |