Taking some REST (part 2)

Taking some REST (part 2)

Gepubliceerd: Categorie: Oracle

[Deze blog is alleen in het Engels beschikbaar]

In my previous post I have described the basic setup for my Proof of Concept project, discussed the basics of REST and the OAuth 2.0 variant I have selected.

As explained, in a regular OAuth 2.0 scenario, this would usually start with an application- or web-based interaction, where the user would be directed by the requesting application to a consent screen of the “resource owner” (that’s Google in this case, this is the entity that owns the task resources I want to be authorized to access on behalf of the user).

So the services described in this part of the blog start after this consent in the form of an “authorization code” has been provided.

Data Structures

As Frederick Brooks wrote in his classic “The Mythical Man Month” – Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowchart; it’ll be obvious. – I will start here with the definitions of the  data structures.

External Service Interfaces

Next, there is an operation defined for getting hold of the token:

External interfaces for obtaining the initial token set and refreshing the access toke

Next, there is an operation defined for getting hold of the token:

Retrieving a stored access token
Coherence cached token

Caching the tokens

As the token information is usually accessed a number of times in short succession, it makes sense to store the token temporarily in a cache. Luckily, SOA Suite 12c comes with a Coherence Adapter, enabling you to easily store XML objects (or POJOs – Plain Old Java Objects) in a designated memory store for fast and easy access.

Ideally, I would store the refresh token obtained from redeeming the authorization code in a persistent data store (like a database) and the transient access token, usually expiring within an hour of obtaining it, in the Coherence cache. However, for this POC it does not matter too much and I decided for the sake of simplicity to store the token as a whole inside the Coherence cache, using the concatenated Issuer (who issue the token – Google) and the owner as the key.

The XML object stored for the token is structured like in the diagram below:

Coherence cached token

Business Services

The data structures for the invoked Google OAuth 2.0 APIs (the business services) are shown below; these structures are NXSDs, XML Schema representation of – in this case – the JSON payloads for the services:

Data Structures for invoking Google’s OAuth APIs

Accessing the APIs

Invoking external services in Service Bus is done by means of a Business Service; in this case, the business service will be created by defining a REST adapter, since the service I’d like to invoke is a REST service:

Defining a REST Business Service by drag and drop from the palette

One of the things to keep in mind when defining a REST adapter is that Oracle will only allow you to use the same HTTP method once per unique REST resource, otherwise it will respond with an error message – that’s why I have created separate business services for redeeming and refreshing, since both use a POST operation against the resource /oauth2/v4/token.

One of the things to keep in mind when defining a REST adapter is that Oracle will only allow you to use the same HTTP method once per unique REST resource, otherwise it will respond with an error message – that’s why I have created separate business services for redeeming and refreshing, since both use a POST operation against the resource /oauth2/v4/token.

Duplicate verb against same resource

Redeeming the Authorization Code

When redeeming the authorization code, Google’s OAuth API expects to receive a URL-encoded POST request message (without payload) and returns a JSON response object; the data structures map onto those described for the business services:

Mapping the inbound request XML to URL-encoded parameter for the outbound request for redeeming the AuthorizationCode for tokens

The outbound request (that is the request to the Google API is sent as an HTTP POST, where the parameters are passed on the URL – there is no payload). For the response however, a JSON payload is received back from the API and this needs to be mapped to an XML data structure to be processed by Service Bus:

Mapping the inbound JSON reply to an XML structure by simply specifying the reply element

The mapping for refreshing the access token follows the same pattern, where the outbound request is mapped onto URL-encoded parameters and the inbound response is mapped from JSON to an XML data structure.

TokenManagement Flows

The finished TokenManagement project has the following structure:

Finished TokenManagement project

As you can see in the picture above, the project has a single (exposed) endpoint proxy service, exposing three operations to:

  • Redeem the initial authorization code (into an access token)
  • Refresh an existing (cached) access token
  • Retrieve an existing access token

Validating the requests

It is a good practice to reject invalid messages as early as possible, so usually one of the first steps in a properly implemented service bus flow is validating the actual request message against the XML Schema Definition:

Validating the requests

It is a good practice to reject invalid messages as early as possible, so usually one of the first steps in a properly implemented service bus flow is validating the actual request message against the XML Schema Definition:

Dynamically validating the inbound request

As you can see, I have not used an if-then-elsif construct to statically validate the different possible request messages for the three operations, but I preferred to hide this behind an XQuery to implement dynamic validation; the XQuery needs to return a prescribed XML structure holding the schema to use for validation, the namespace for the element and the actual element name itself:

  1. xquery version "1.0" encoding "utf-8";
  2. (:: OracleAnnotationVersion "1.0" ::)
  3. declare variable $operation as xs:string external;
  4. declare function local:func($operation as xs:string) {
  5. <validate xmlns="http://www.bea.com/wli/sb/context">
  6. <schema>TokenManagement/Schemas/Authorization</schema>
  7. <schemaElement>
  8. <namespaceURI>http://xmlns.qualogy.com/blog/mnuman/OAuth</namespaceURI>
  9. <localname>{ if ($operation = 'RedeemAuthorizationCode')
  10. then 'RedeemAuthorizationTokenRequest'
  11. else if ($operation = 'RefreshAccessToken')
  12. then 'RefreshAccessTokenRequest'
  13. else if ($operation = 'GetAccessToken')
  14. then 'GetAccessTokenRequest'
  15. else 'CANNOTBEPROCESSED'
  16. }
  17. </localname>
  18. </schemaElement>
  19. </validate>
  20. };
  21. local:func($operation)

After validating the request (and only if the validations completes successfully – otherwise an error will be raised, resulting in a SOAP Fault being returned to the caller), the message will be dispatched using an operation branch (yes, I could have used dynamic routing here as well – but I wanted to show how cluttered the flow can become by these parallel options):

Redeeming the AuthorizationCode

Redeeming the authorization code simply works by performing the callout to the Google OAuth API; the inbound request is transformed for the business service by using an XQuery function, where an XQuery library module provides the common values for all requests, like the client_id and the client_secret.
After successfully obtaining the access tokens, a cache token is created from it and this is stored in-memory using the Coherence Adapter, specifying a manual key composed of the concatenation of Issuer and Owner fields (from the request). The service itself only returns the access token to the caller.

Refreshing the Access Token

Since the access tokens usually expire after some time (for Google this is one hour), the tokens need to be refreshed occasionally. The operation for refreshing the access token first retrieves the currently cached token from memory, as this contains the special token needed for obtaining a new, refreshed token. If this token is not found in the cache, a SOAP Fault is returned. If the token is found, this is used to build an outbound request for the Google API, again leveraging the XQuery Module inside another XQuery for building the request. After a successfull request, the renewed access token is stored in the in-memory cache and the access token itself will be returned by the service.

Getting the Access Token

The implementation for getting the access token first retrieves the access token from the in-memory cache; this operation can have three different outcomes:

  • the Cache token cannot be found: this situation will trigger a SOAP Fault to be returned
  • the Cache token is found, but the system date is beyond the expiration date: in this cache, the access token will be returned from the refresh access token that is called in this location
  • the Cache token is found and still valid: this is the simple scenario where the access token will be extracted and returned.

Summary

In this blog post I have shown how to do some basic token management for Google’s OAuth 2.0 implementation, caching the tokens in-memory for performance (robustness requires a more hybrid approach). The operations are exposed internally, in our service landscape, as SOAP Services accessible over HTTP.

In the third and final part of this series, I will show how to actually use these OAuth 2.0 tokens to invoke some secured resources from Google. You can read more about this here.

Milco Numan
Over auteur Milco Numan

Technical Consultant, specialized in integration and design/development of extensions to Oracle e-Business Suite using Developer (Forms/Reports), Workflow, PL/SQL, Java (OAF) and SOA Suite 10g/11g/12c. Certified in Java (SCJP for Java 5), Oracle SOA Suite 11g, Oracle SOA Suite 12c, Oracle BPM Suite 11g, Oracle IT Architecture SOA 2013 Essentials & Oracle Enterprise Linux 6, SOA Architect & SOA Security Specialist (SOASchool.com). View my LinkedIn profile: nl.linkedin.com/in/milconuman/

Meer posts van Milco Numan
Reacties
Reactie plaatsen