Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
nicoschoenteich
Developer Advocate
Developer Advocate
This SAP Tech Byte is about how to use the SAP Approuter in combination with another Node.js based application and set up proper authentication between the two using JSON Web Tokens. In this scenario, we benefit from SAP Approuter capabilities such as easy connections to destinations and the XSUAA service (Authorization and Trust Management Service), while also having the freedom to build any Node.js application we want (for example a server-side rendering NuxtJS application).

The source code for this blog post can be found at https://github.com/SAP-samples/sap-tech-bytes/tree/cloud-foundry-basics/post5.

Note: I will use the terms "validate" and "verify" interchangeably in this blog post.

 

Background


As already discovered in the previous blog posts, the SAP Approuter is usually used to route users to static resources in the form of local directories and destinations. In doing so, the Approuter abstracts the authorization and token flow (think magic) - making these concepts really easy to implement and more secure. The Approuter itself is a Node.js based application that contains an xs-app.json file describing its routing behavior. In the most common scenario, this xs-app.json file points to a local directory or a remote repository containing a static web page (HTML, CSS, JavaScript). But what if we want to forward requests to a second server application that is separately deployed and reachable via a URL? As far as the Approuter is concerned, this is not a problem at all. We simply define a new route in the xs-app.json that points to a destination, and that destination points to the URL of the second application. But we do have a problem: How do we prevent users from accessing this second application directly? In theory, users could find this URL and access it - bypassing the Approuter and its authorization flow - not good! This is where the validating JSON Web Tokens comes into play.

This is essentially what we want to do:


In this scenario, the second application (on the right) is a server-side rendering NuxtJS application. This application has one key obligation, and that is validating the JWT that it receives from users. Let's take a closer look at the individual parts of our architecture.

 

SAP Approuter


Let's first look at the left half of the architecture.

The SAP Approuter is a pretty simple Node.js based application that acts as the main entry point of our architecture. It redirects users to the Authorization and Trust Management Service (XSUAA), where users login and receive a JSON Web Token (JWT), before being redirected back to the Approuter. The Approuter then forwards all request to a destination. This is all configured via the xs-app.json file:
{
"authenticationMethod": "route",
"routes": [
{
"source": "^(.*)",
"target": "$1",
"destination": "nuxt",
"authenticationType": "xsuaa"
}
]
}

 

The "nuxt" destination is added to the Approuter via its environment variables. The destination points to the URL of the NuxtJS application and is configured with forwardAuthToken = true to forward the user's authorization token (JWT). This ensures that the JWT will be included in every request that goes from the Approuter to the NuxtJS application (in the req.header.authorization object):
{"name":"nuxt", "url":"https://nuxt.cfapps.us10-001.hana.ondemand.com", "forwardAuthToken": true}

 

The XSUAA service instance is created using the Cloud Foundry CLI. This has to be done before deploying the Approuter, as the Approuter requires the XSUAA service instance during deployment.

 

NuxtJS Application


Let's now look at the right half of the architecture.

Now this is where things get interesting. As already mentioned previously, we have to prevent users from bypassing the Approuter and accessing the NuxtJS application directly. After all, we want all users to first authenticate against the XSUAA instance before being able to interact with our application. The NuxtJS framework has a very convenient way of handling middleware (see named middleware), as you can simply add files to the middleware/ directory that will always get executed first when accessing the application. So by creating a middleware/auth.js file and referencing it in the nuxt.config.js we have the perfect place to check if a valid JWT is present in the request header.

To validate a JWT, we can use a combination of Node.js packages provided by SAP. First, we use @sap/xsenv to load the XSUAA environment our NuxtJS application is bound to via an application binding in Cloud Foundry (see its manifest.yaml). An application binding essentially means that service instance credentials are stored in the environment variables of an application, which is why the corresponding method is fittingly called xsenv.loadEnv(). Now that we have the XSUAA environment, we can use the second package @sap/xssec to create a security context and pass it the token we received via the req.header.authorization object from the Approuter. The xssec.createSecurityContext() method sends a request to the XSUAA instance that validates it. This is a so called "online validation". If there is no token or the token is invalid, an unauthorized error is returned. If a valid token is present, the middleware is exited without errors and the user gets to the UI of the application. This what the whole middleware/auth.js file looks like:
var xssec = require('@sap/xssec')
var xsenv = require('@sap/xsenv')

export default function (context) {

const {
app,
store,
route,
params,
query,
env,
isDev,
isHMR,
redirect,
req,
error,
$config
} = context

const unauthorized = () => {
return error({
statusCode: 401,
message: "401 - Unauthorized"
})
}

if (!req.headers.authorization) {
unauthorized()
} else {
// splitting "Bearer " from the header
const token = req.headers.authorization.split(" ")[1]
xsenv.loadEnv()
xssec.createSecurityContext(
token,
xsenv.getServices({ xsuaa: { tag: 'xsuaa' } }).xsuaa,
function (error, securityContext, tokenInfo) {
if (error) {
unauthorized()
return
}
})
}
}

 

There is also the possibility to validate the token offline with a package like jsonwebtoken (see method jwt.verify()). In this case, the public key of the XSUAA instance is used validate the signature of a token via an algorithm - all done locally without an external request (offline).

 

Developing Locally


The described architecture requires application bindings of the Approuter and the NuxtJS application to the XSUAA instance. You might be wondering how this can be achieved locally. As already stated earlier, an application binding is essentially achieved by storing the service instance credentials in the environment variable of an application. We can do exactly that to run the scenario locally.

 

First, we have to deploy the whole architecture once, so that Cloud Foundry generates the environment variables. To do that, run the following command in the approuter/ directory:
sh create-service.sh && cf push

 

Once the Approuter is deployed, run the following command in the nuxt/ directory:
npm install && npm run build && cf push

 

Now we can get the environment variable from the deployed Approuter:
cf env approuter-nuxt

 

Copy the output (only the VCAP_SERVICES object is required) in a new default-env.json in the approuter/ directory. Also add the required destination information and the port environment variable to make sure our Approuter runs on the port that is whitelisted for the XSUAA instance. The file should look like this:
{
"PORT": 5001,
"destinations": [
{
"name": "nuxt",
"url": "https://nuxt.cfapps.us10-001.hana.ondemand.com",
"forwardAuthToken": true
}
],
"VCAP_SERVICES": {
"xsuaa": [ ... ]
}
}

 

We can now continue with the NuxtJS application and get its environment variables:
cf env nuxt

 

Again, copy the output (only the VCAP_SERVICES object is required) in a new default-env.json - but this time in the nuxt/ directory. The file should look like this:
{
"VCAP_SERVICES": {
"xsuaa": [ ... ]
}
}

 

Now we can start both applications in separate terminals. Run the following command in the approuter/ directory:
npm start

 

Run the following command (in a separate terminal) in the nuxt/ directory:
npm run dev

 

Voila, we now have set-up both application to run just like they do in the cloud and can start developing. You should notice you get redirected to the XSUAA instance when accessing the Approuter at http://localhost:5001.

I hope you enjoyed this blog post. Hopefully you learned a little something about how to verify JSON Web Tokens from the SAP Approuter or an XSUAA instance more generally.

Feel free to reach out in case you have any questions.

Stay tuned for more blog posts covering the basics of Cloud Foundry.

 

 






 

 

 

 

SAP Tech Bytes is an initiative to bring you bite-sized information on all manner of topics, in video and written format. Enjoy!

 

 

 
8 Comments