Skip to Content

At the end of part 1 of this blog, the decision was taken to use the Open Authorization Framework (OAuth) for protecting the API exposed by the application on HCP. By obtaining an access token from the OAuth 2.0 Authorization Server (AS) on HCP, my sensors (thermostats) connected to the openHAB instance running on my single board computer get authorized to consume the API and store their measured temperature values in the Cloud. So let’s get started!

Authorizing the thermostats with OAuth

In general, obtaining an OAuth access token from HCP’s AS follows the OAuth 2.0 specification’s authorization code grant flow:

  1. The account administrator in the HCP account must register an OAuth client (the API consumer, in our scenario the openHAB instance acting on behalf of the sensors connected to it) in the HCP Cloud Cockpit. This requires to specify a “Redirect URI” for the client which is used in step 4 of this process.
  2. The OAuth client directs the user’s agent (i.e. the web browser) to the AS authorization endpoint, which requires the user to authenticate with their HCP account’s trusted identity provider (SAP ID Service, the customer’s SAP Cloud Identity tenant, or their own identity provider).
  3. Upon successful login with the identity provider (IdP), the user is asked by the AS to confirm that the OAuth client requesting a token will be allowed to act on his or her behalf, optionally under a specific scope (e.g. “send sensor data” in this IoT scenario)
  4. If the user confirmed the previous step, the AS uses the URI registered in step 1 to redirect the user’s web browser back to the OAuth client. The HTTP redirect URL includes a one-time authorization code generated by the AS.
  5. The OAuth client request an OAuth access token from the AS token endpoint with the authorization code obtained in the previous step.
  6. The AS validates the authorization code, issues a new access token, and sends it back to OAuth client. The AS associates the token with the user who confirmed the OAuth client’s authorization request in step 3.
  7. By sending the access token with the API call in the HTTP Authorization header according to the OAuth specification, the OAuth client can make an authorized call to the API protected with OAuth on HCP.

OAuth 2.0 improves the overall security of the scenario by avoiding to pass the user’s confidential username and password to the OAuth client. Using OAuth, the user only shares their credentials with the identity provider (IdP). Although the OAuth client still has to take care for secure storage of the issued access token in step 6, the potential damage of a stolen token compared to a stolen password is considerably lower. The access token only authorizes an OAuth client to call a single API endpoint on behalf of the user. A username and password has a much broader scope – it may allow access to a large number of web sites and services.

Using the OAuth 2.0 Authorization Code Grant Flow in the IoT scenario

Looking at steps 2, 3 and 4 above shows that the authorization code grant flow relies on the user’s web browser to obtain the authorization code from the AS and redirect back to the OAuth Client. Starting off this process from the central control unit won’t work – it is a headless device with no web browser installed on it. Therefore we make use of special feature in HCP’s OAuth AS, which facilitates the authorization of an OAuth Client on a mobile device using a QR code.

 

Here is how we will change the above process to authorize the sensors in our IoT scenario:

 

Step 1: OAuth Client Registration

 

We’ll register OAuth clients in the Cloud Cockpit for each sensor because we want to authorize them individually. So in our case, we’ll have three OAuth clients: “sensor1” for the thermostat in the living room, “sensor2” for the kitchen’s thermostat, and “sensor3” for the bath’s thermostat. All of them are configured with the same redirect URI, which will only play a minor role in the following steps. Please note that the OAuth clients must be assigned to a subscription in the account, which is the UI5 application used later to display the temperature values. So this step assumes that the application is already deployed in the account.

/wp-content/uploads/2015/02/clients_644663.jpg

 

Optionally one can register application-specific OAuth scopes under the application’s submenu folder in the Cockpit:

 

/wp-content/uploads/2015/02/scopes_644699.jpg

 

Step 2: Obtaining the Authorization Code with a QR Code from the mobile device

 

Instead of starting the Authorization Code Grant Flow from the OAuth Client launching a web browser which wouldn’t be possible from my openHAB instance running on the headless single board computer, we stay in the Cloud Cockpit and open another browser window to access HCP’s OAuth AS End User UI.

/wp-content/uploads/2015/02/oauthui_644665.jpg 

This requires to login to the OAuth AS, which is done in my case using an SCI tenant:

/wp-content/uploads/2015/02/login_644681.jpg

With a click on “Code” in the OAuth AS UI you can generate a new authorization code for a selected OAuth client. To simplify the transfer of the authorization code to the OAuth Client which needs it to request the access token, a QR code for the new authorization code will be generated as well.

/wp-content/uploads/2015/02/qr_644700.jpg

Having a QR code scanner installed on my mobile device from where I also want to remotely control my thermostats, I can easily scan the generated code and store it in my smart phone’s clipboard.

Now comes the tricky part: How should the scanned code be passed to openHAB so that it can request the access token from HCP’s OAuth AS in order to persist the thermostat’s data in the Cloud? All I would need is an input field on the thermostat’s element in openHAB’s mobile app UI which allows me to paste the authorization code. openHAB would have to take it from there and use it to send the access token request and store it at a secure place on the local (my Cubietruck’s) file system. Unfortunately, input fields are not in openHAB’s supported list of standard UI elements for a sitemap. Fortunately, there is an WebView element which allows you to refer to a URL of your choice and display the content in a frame within the mobile UI. If this URL will point to a simple Java servlet running on openHAB generating a label and input field to paste the code with a button to submit it, we are almost done!

Step 3: Extending openHAB to capture the authorization code and request the access token from HCP

So this leaves us with extending openHAB with a new binding implementing the servlet to capture the scanned authorization code from the clipboard and requestthe access token with it. A good starting point for developing a new openHAB binding are these instructions on the OpenHAB wiki. With the OSGi bundle skeleton generated by Maven for my new “AuthzCode” binding, I first add a Servlet class to it:

package org.openhab.binding.authzcode;
...
public class WebViewServlet extends HttpServlet {
private static final String SERVLET_NAME = "/webview";
    ....
/**
     * Activates the webview servlet.
     */
    protected void activate() {
        try {
            logger.debug("Starting up authzcode webview servlet at " + SERVLET_NAME);
            Hashtable<String, String> props = new Hashtable<String, String>();
            httpService.registerServlet(SERVLET_NAME, this, props, createHttpContext());
            bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
            if (bundleContext != null) {
                ServiceReference<?> serviceReference = bundleContext.getServiceReference(AuthzCodeBindingProvider.class.getName());
                if (serviceReference != null) {
                    bindingprovider = (AuthzCodeBindingProvider) bundleContext.getService(serviceReference);
                } else
                    logger.error("BindungProvider is null");
                // get all items for this binding
                for (String itemName : bindingprovider.getItemNames()) {
                    Item sapHcpItem = itemUIRegistry.getItem(itemName);
                    logger.debug("Found item: " + sapHcpItem.getName());
                }
            } else
                logger.error("bundleContext is null");
        } catch (Exception ex) {
            logger.error("Error during saphcp-webview servlet startup", ex);
        }
    }
    /**
     * Deactivates the webview servlet.
     */
    protected void deactivate() {
        httpService.unregister(SERVLET_NAME);
    }
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        logger.debug("Received WebView Request");
        String action = request.getParameter("action");
        String request_clientid = request.getParameter("clientid");
        if (StringUtils.isNotBlank(request_clientid)) {
            this.clientid = request_clientid;
        }
        logger.debug("clientid set to: "  + clientid);
        if (StringUtils.isNotBlank(action) && action.equalsIgnoreCase("sendcode")) {
            logger.debug("Executing action: " + action);
            String code = request.getParameter("code");
            // create command (string)
            String commandString = "authorize " + clientid + " " + code;
            StringType command = new StringType(commandString);
            if (bindingprovider.getItemNames() != null) {
                // send command synchronously
                String firstItemName = bindingprovider.getItemNames().iterator().next();
                eventPublisher.sendCommand(firstItemName, command);
            } else {
                logger.warn("No item with authzcode binding found");
            }
        }
        response.getWriter().println(buildFormString());
    }
    private String buildFormString() {
        String result = "<html>";
        result += "<head><style>p {font-family:verdana;}</style></head>";
        result += "<body><form action='./webview' method='get'>";
        result += "<p>Code <input type='text' name='code'>";
        result += "<input type='hidden' name='action' value='sendcode'>";
        result += "<input type='submit' value='Submit'></p></form>";
        // update state
        // check if file with token exists
        String accessToken;
        accessToken = AuthzCodeCommons.loadToken(this.clientid);
        if (StringUtils.isBlank(accessToken)) {
            result += buildAccessTokenFailed();      
        } else {
            result += buildAuthorizedString();
        }
        result += "</body></html>";
        return result;
    }
    private String buildAuthorizedString() {
        return "<p><span style=\"color:green\">Sensor is authorized!</span></p>";
    }
    private String buildAccessTokenFailed() {
        return "<p><span style=\"color:red\">Sensor is not authorized!</span></p>";
    }
}



















If no request parameter is sent, the doGet method generates a simple HTML form which shows an input field to paste the authorization code in. The form also informs the user if there is already an access token stored for the current item (sensor) or not. By pressing the Submit button, a request is sent to the servlet with two request parameters: The action to send the received code to HCP, and the id of the OAuth Client (thermostat) for which the access token should be requestd. In this case, the WebViewServlet sends a command “authorize” via its EventPublisher instance to the openHAB bus and passes the client id and authorization code with it (lines 60-67).

 

To launch the servlet when the bundle get loaded and activated, a new component description (webviewservlet.xml) for the servlet is added in the OSGI-INF folder of the bundle:

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="activate" deactivate="deactivate" name="org.openhab.authzcode">
   <implementation class="org.openhab.binding.authzcode.WebViewServlet"/>
   <reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="dynamic" unbind="unsetHttpService"/>
   <reference bind="setItemUIRegistry" cardinality="1..1" interface="org.openhab.ui.items.ItemUIRegistry" name="ItemUIRegistry" policy="dynamic" unbind="unsetItemUIRegistry"/>
   <reference bind="setEventPublisher" cardinality="1..1"
  interface="org.openhab.core.events.EventPublisher" name="EventPublisher"
  policy="dynamic" unbind="unsetEventPublisher" />
</scr:component>




















This component description must be referenced in the bundle’s MANIFEST.MF file via the Service-Component property:

Service-Component: OSGI-INF/binding.xml, OSGI-INF/genericbindingprovider.xml, OSGI-INF/webviewservlet.xml




















Now it is up to the AuthzCode binding’s main component, the Maven-generated AuthzCodeBinding class, to execute the “authorize” command and request the access token with the passed code and client (sensor) id from HCP. This is done by implementing the internalReceiveCommand method, which uses an Apache Commons HTTPClient to create a POST request to the HCP OAuth AS token endpoint according to the OAuth 2.0 specification. In case of a successful access token request, the response is a JSON formattet string containing the access token which is finally stored on the openHAB instance.

public class AuthzCodeBinding extends AbstractBinding<AuthzCodeBindingProvider> implements ManagedService {
...
  @Override
    protected void internalReceiveCommand(String itemName, Command command) {
        // the code being executed when a command was sent on the openHAB
        // event bus goes here. This method is only called if one of the
        // BindingProviders provide a binding for the given 'itemName'.
        // process command
        if (command instanceof StringType) {
            StringType t = (StringType) command;
            logger.debug("internalReceiveCommand() is called with command \"" + t + "\"");
            String[] commandParts = StringUtils.split(t.toString());
            if (commandParts[0].equals("authorize")) {
                String clientId = commandParts[1];
                String authzCode = commandParts[2];
                this.authorize(clientId, authzCode);
            }
        }
    }
       
    private void authorize(String clientId, String authzCode) {
        logger.debug("authorize() method is called with value " + authzCode);  
        HttpClient httpClient = new HttpClient();    
        if (this.proxyHost != null) {
            httpClient.getHostConfiguration().setProxy(this.proxyHost, new Integer(this.proxyPort).intValue());
        }   
        PostMethod httpPost = new PostMethod(this.tokenendpoint);
        httpPost.addRequestHeader("content-type", "application/x-www-form-urlencoded");
        httpPost.addParameter("client_id", clientId);
        httpPost.addParameter("grant_type", "authorization_code");
        httpPost.addParameter("code", authzCode);
        httpPost.addParameter("redirect_uri", this.redirectUri);       
       
        BufferedReader br = null;       
        try{
            int returnCode = httpClient.executeMethod(httpPost);
            if(returnCode != HttpStatus.SC_OK) {
              // still consume the response body
              String errorResponseBody = httpPost.getResponseBodyAsString();
              logger.error("Failed to request access Token" + errorResponseBody);
            } else {
                // Read the response body.
                byte[] responseBody = httpPost.getResponseBody();
                // parse response for token
                Object accessTokenResponse = JSONValue.parse(new String(responseBody));
                JSONObject array=(JSONObject)accessTokenResponse;
                // store access token
                AuthzCodeCommons.saveToken(clientId, (String)array.get("access_token"));
            }
          } catch (Exception e) {
            logger.error("Failed to store access token for client " + clientId + ": " + e.getMessage());
          } finally {
            httpPost.releaseConnection();
            if(br != null) try { br.close(); } catch (Exception fe) {}           
          }
    }
...

















Static configuration parameters of the AuthzCode binding such as the HCP OAuth AS token URL or optional HTTP proxy settings if openHAB is operated behind a firewall can be set in openHAB’s central configuration (openhab.cfg), which is read when the system is started and the bindings are initialized.

Adding the new items for authorizing the three thermostats starts with defining them in openHAB’s items configuration file:

...
String Authz_Thermostat_Kitchen {authzcode}
String Authz_Thermostat_Livingroom {authzcode}
String Authz_Thermostat_Bath {authzcode}







To expose them as a WebView in openHAB’s mobile app, the new items are referenced in the sitemap configuration file from part 1 of this blog as follows:

sitemap homezone label="Homezone"
{
    Frame {
        Text item=Thermostat_Livingroom_Actual {
            Frame {
                Setpoint item=Thermostat_Livingroom_Target step=0.5 maxValue=28
                Webview item=Authz_Thermostat_Livingroom url="http://<openHAB host>:<port>/webview?clientid=sensor1" height=2
            }
        }
    }
...






From the url parameter of the WebView element which points to the binding’s servlet, the sensor’s OAuth client ID is passed as a request parameter (clientid) to the binding.

Step 4: Using the access token to persist sensor data on HCP

With the access token in place to authorize a sensor to persist its data on HCP, we need a component on openHAB to actually call our API whenever there is new temperature value captured by a thermostat and sent on the event bus. Like with our new binding, there is no such component in openHAB’s Add-On package which can do this out-of-the box. Since this sounds like a reusable component which may be useful for any kind of sensor data (not just my thermostat’s temperature values) to persist on HCP, I created a new openHAB action (bundle) for it, which I called “sapHCP”.

A skeleton bundle for a new openHAB action is built almost the same way as the binding before using a Maven archetype. The action’s static method sendRequest will be called by openHAB  via a rule which is triggered by an sensor value update event on the bus. More on this in a minute! One can also pass parameters when invoking an action’s static method. In case of the sapHCP action, those are the actual sensor (temperature) value to store on HCP, the unit of the value (e.g. Celsius), the sensor type (e.g. Thermostat), an optional description and the sensor’s OAuth Client ID.

package org.openhab.action.saphcp.internal;
...
public class SapHCP {
    ...
    public static boolean sendRequest(String sensorValue, String unit, String type, String description, String clientId) {
        ...
        // check for file saphcp.token in etc directory
        String accessToken = AuthzCodeCommons.loadToken(clientId);
        if (StringUtils.isNotEmpty(accessToken)) {
            //     found the token
            logger.debug("Found token for client with id " + clientId);
            //  post new temperature value to all HCP items
            HttpClient httpClient = new HttpClient();
            if (SapHCP.defaultProxyHost != null && SapHCP.defaultProxyPort != null) {
                httpClient.getHostConfiguration().setProxy(SapHCP.defaultProxyHost, new Integer(SapHCP.defaultProxyPort).intValue());
            }
      
            PostMethod httpPost = new PostMethod(SapHCP.defaultAPIUrl);
            // add access token as authorization header
            httpPost.addRequestHeader("Authorization", "Bearer " + accessToken);
            JSONObject newSensorValue=new JSONObject();
            newSensorValue.put("value", sensorValue);
            newSensorValue.put("sensorId", clientId);
            newSensorValue.put("unit", unit);
            newSensorValue.put("type", type);
            newSensorValue.put("description", description);
            try {
                StringRequestEntity requestBody = new StringRequestEntity(newSensorValue.toJSONString(), "application/json", "UTF-8");
                httpPost.setRequestEntity(requestBody);      
                BufferedReader br = null;      
                try
                {
                    int returnCode = httpClient.executeMethod(httpPost);
                    // read response
                    if(returnCode != HttpStatus.SC_OK) {
                        // still consume the response body
                        String errorResponseBody = httpPost.getResponseBodyAsString();
                        logger.error("Failed to send new temperature value: " + errorResponseBody);
 
                    } else {
                        // Read the response body.
                        byte[] responseBody = httpPost.getResponseBody();
                        // parse response
                        logger.debug("New temperature value stored. ID: " + new String(responseBody));
                    }
                } catch (Exception e) {
                    logger.error("Unknown Host Exception: " + e.getMessage());
                } finally {
                      httpPost.releaseConnection();
                      if(br != null) try { br.close(); } catch (Exception fe) {}
                }
            } catch (UnsupportedEncodingException uee) {
                logger.error("JSON encoding error: " + uee.getMessage());
            }
        } else {
            logger.warn("No Access token found");
        }
        return true;
    }
}














Using the OAuth client ID parameter as an search index, the sapHCP action can retrieve the sensor’s OAuth access token which has been requested in the previous step with the AuthzCode binding and stored in a local token store. If an access token was found for the sensor (client), it is passed with the HTTP authorization header (line 24) in the API call to HCP. The API on HCP expects a flat JSON structure with the values passed as parameters to the sapHCP action.

Step 5: Triggering the sapHCP action from an openHAB rule

Besides the already mentioned items and sitemap configuration files in part 1 of this blog, actions triggered by events on openHAB’s software bus can be defined in a rules file to automate processes. The rules for my home automation scenario are quite simple: Whenever a thermostat sends an update on the actual temperature (item), an sapHCP action should be triggered to send the new sensor value to HCP.

import org.openhab.core.library.types.*
import org.openhab.model.script.actions.*
import org.openhab.action.saphcp.*
rule "Send Living Room updates to HCP"
when
    Item Thermostat_Livingroom_Actual received update
then
    var value = Thermostat_Livingroom_Actual.state.toString
    logInfo("sapHCP","Sensor 1: " + value)
    sendRequest(value, "C", "Thermostat", "Living Room", "sensor1")
end
...












The above excerpt from the rules file show the descriptive rule definition language in openHAB: In case the item representing the actual temperature in the living room receives a temperature update, its current value is stored in a local variable and send to HCP using the sapHCP action’s sendRequest method.

Step 6: Protecting the API on HCP with OAuth

The last remaining piece of the puzzle is the UI5 application running on HCP which exposes the API consumed by the openHAB sapHCP action. Following the declarative approach for protecting the API using OAuth results in the following filter configuration in the application’s web.xml deployment descriptor:

...
<filter>
    <display-name>Sensor OAuth Protection</display-name>
    <filter-name>PersistDataScopeFilter</filter-name>
    <filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
    <init-param>
      <param-name>scope</param-name>
      <param-value>persist-data</param-value>
    </init-param>
    <init-param>
      <param-name>http-method</param-name>
      <param-value>post</param-value>
    </init-param>
    <init-param>
      <param-name>no-session</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>PersistDataScopeFilter</filter-name>
    <url-pattern>/api/v1</url-pattern>
  </filter-mapping>
...












The API itself is implemented using the the JAX-RS framework Jersey to annotate my MeasurementResource class. Jersey’s application servlet is mapped to the path “/api/v1”.

@Path("measurement")
@Produces({ MediaType.APPLICATION_JSON })
public class MeasurementResource {
    private static Logger logger = LoggerFactory.getLogger(MeasurementResource.class);
    @GET
    public Response getAllMeasurements()
    {
        logger.debug("getTemperatures() called");
        MeasurementDAO measurementDAO = new MeasurementDAO();
        List<Measurement> measurements = measurementDAO.getAllMeasurements();
        return Response.ok().entity(measurements).build();
    }
    @GET
    @Path("sensors")
    public Response getSensors()
    {
        logger.debug("getSensors() called");
        Collection<Measurement> resultList = new ArrayList<Measurement>();
        MeasurementDAO measurementDAO = new MeasurementDAO();
        List<String> sensorIDs = measurementDAO.getSensorIDs();
        for (String sensorID : sensorIDs) {
            Measurement lastMeasurementForSensor = measurementDAO.getLastMeasurementForSensor(sensorID);
            resultList.add(lastMeasurementForSensor);
        }
        return Response.ok().entity(resultList).build();
    }
    @GET
    @Path("sensor/{sensorId}")
    public Response getMeasurementsForSensor(@PathParam("sensorId") String sensorId) {
        MeasurementDAO measurementDAO = new MeasurementDAO();
        List<Measurement> measurementsForSensor = measurementDAO.getSensorMeasurements(sensorId);
        return Response.ok().entity(measurementsForSensor).build();
    }
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response addMeasurement(Measurement newMeasurement)
    {
        logger.debug("addTemperature() called");
        MeasurementDAO measurementDAO = new MeasurementDAO();
        long measurementId = measurementDAO.addMeasurement(newMeasurement);
        return Response.ok().entity(measurementId).build();
    }
}











All methods make use of a central Data Access Object (DAO) to read and store the sensor measurements from the HANA DB on HCP. Internally, the DAO consumes the HCP Persistence Service and uses JPA to manage all sensor data in one single table. Only the addMeasurement operation of the API annotated with the @POST resource method is consumed from the sapHCP action in openHAB. The other read-only operations such as getMeasurementsForSensor are actually consumed from the SAP UI5 JSON models in the application’s user interface. In order to protect them as well from unauthorized access, the following security constraint to require FORM-based authentication with the account’s (SAML) Identity Provider for the API’s path “/api/v1/*” is added to the application’s web.xml:

  <login-config>
    <auth-method>FORM</auth-method>
  </login-config>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected Web UI Area</web-resource-name>
      <url-pattern>/dashboard.html</url-pattern>
      <url-pattern>/api/v1/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>Everyone</role-name>
    </auth-constraint>
  </security-constraint>










To also allow in parallel the consumption of the API’s POST-method from openHAB which cannot login with SAML but provides an OAuth access token, the login stack for FORM must be configured in the Cloud Cockpit to include the OAuth 2.0 Login Module:

/wp-content/uploads/2015/02/authnconfig_645396.jpg

That’s it! The following video shows the end-to-end flow with all components developed and used in this blog series:

  • AuthzCode binding and sapHCP action on openHAB
  • the openHAB mobile application on my smart phone
  • the SAP UI5 application and the OAuth-protected REST API deployed in my HCP trial account
  • my SCI tenant for user authentication at the UI5 application and the HCP OAuth 2.0 AS

To report this post you need to login first.

9 Comments

You must be Logged on to comment or reply to a post.

  1. Chris Paine

    Hi Martin,

    Awesome blog post – I can see you had a lot of fun putting this together! I’ve looked at using a joint OAuth, SAML, or basic authenticated API – but cannot find a way to this on the local HCP build – only the “real” one – is there some way to change the authentication configuration on the local HCP servers for testing purposes?

    What I’ve ended up doing is having an unauthenticated servlet that calls the OAUTH login context and attempts to log the user in using any Authentication: bearer <access token> headers that they have, which then generates cookies that can be used to authenticate to all the other FORM based logins. This seems to work reasonably well for me currently, although just being able to flag all my API endpoints as OAuth accessible (and be able to test this locally) would be great!.

    Cheers,

    Chris

    (0) 
    1. Martin Raepple Post author

      Hi Chris,

      yes, it has been – and continues to be – a lot of fun! There is so much more to explore: Voice control, presence detection, … 😀

      – but cannot find a way to this on the local HCP build – only the “real” one – is there some way to change the authentication configuration on the local HCP servers for testing purposes?

      Have you tried to change the login stack for FORM in the local server? You can find the JAAS config in your local server’s directory \config_master\com.sap.security.auth.service\java.login.conf

      Look for the FORM stack in this file, and try to add the OAuth2 Login Module at the end to it like this:

      FORM {

         com.sap.security.auth.module.basic.PasswordLoginModule sufficient;

         com.sap.core.jpaas.security.saml2.sp.loginmodule.SAML2JPaaSLoginModule sufficient mode=passive;

         com.sap.cloud.security.oauth2.loginmodule.OAuth2JPaaSLoginModule sufficient;

      };

      I’ve tested this with my local Tomcat 7 server. If the OAuth Login Module is missing in the stack, I get the HTML page of the local server’s login screen as a response to my REST call (even with a valid access token issued by the local OAuth AS). If the OAuth login module is in, I get the response from the API with the data.

      Hope this helps!

      Martin

      (0) 
    2. Rui Nogueira

      Martin and I have become the de-facto home-automation gurus in the HCP team.

      You should have seen our Death Start home automation system up-and-running 2 weeks ago at an SAP internal event 🙂

      Best,

      Rui

      (0) 
  2. Christian Häussler

    Hallo Martin und Rui,

    habt Ihr geplant Euer “AuthzCode” Binding im openHAB Projekt zu contributen?

    Das wäre schön. Ich könnte es evtl. auch übernehmen, wenn Ihr mir die Sourcen zur Verfügung stellt.

    Danke und viele Grüße,

    Chris

    (0) 
    1. Martin Raepple Post author

      Hi Chris,

      thanks for your interest in the binding. We actually have built two components in this sceanrio:

      1. The AuthzCode binding which we needed to support the OAuth 2.0 Authorization Code Grant flow in openHAB. Since we made the authorization and token endpoint of the OAuth 2.0 Authorization Server configurable in the openhab.cfg file, it should be a pretty generic binding which will also work with other (Cloud Platform) OAuth servers, not just on HCP.
      2. The sapHCP action, which is basically also a general purpose OAuth client using the access token obtained via the above binding.

      We also thought about contributing both components to openHAB if there is a real demand for it in the openHAB community. Another good home I can think of for the complete code would also be on SAP’s GitHub (SAP · GitHub). I’ll update the blog when we finalized our thoughts and published the complete code.

      Best regards

      Martin

      (0) 
  3. Christoph Barstorfer

    Hello,

    if I try to create a new oauth client, I get following messge:

    Could not save client data.forbidden (403)

    Are there any limitations on hcp trial?

    regards,

    Christoph

    (0) 
    1. Martin Raepple Post author

      Hi Christoph,

      there are no limitations on HCP trial for registering OAuth clients. Most likely you did not select the correct application/subscription when you tried to create the client. Please make sure that you register the client for the application you deployed in your trial account, and do not select any of the services subscriptions (e.g. the HTML5 dispatcher or HCPms).

      Best regards

      Martin

      (0) 
      1. Christoph Barstorfer

        Hi Martin,

        you are right, I’ve selected “services/dispatcher” and ODATA-URL as Redirect-URI.

        In my scenario I try to GET/POST data via ODATA (..xsodata in HANA XS Application). ODATA works fine in web browser and Postman REST client.

        Is it possible to use OAuth for authentication in this case? How to create „Subscription“ for client registration?

        Best regards,

        Christoph

        (0) 
        1. Martin Raepple Post author

          Hi Christoph,


          OAuth is not supported on XS. Only Java-based services can be protected using the OAuth Authorization Server on HCP.


          Best regards

          Martin

          (0) 

Leave a Reply