You’ve reached the very right blog for you,
If you see this:
Forbidden
And:
If you hate it
In this blog, we’re going to learn how to do troubleshooting when we get the
Forbidden
Of course, this blog covers only scenarios reaching out to
SAP Cloud Platform Backend service, as described in this
series of tutorials
What is the problem?
We’ve created an API with
SAP Cloud Platform Backend service.
As we’ve learned (e.g.
here) the APIs are protected with OAuth (see
here and
here for explanations)
This means, when such a resource is requested, an access token has to be sent
But what, if we get an error response?
The error message could be:
Forbidden
What now?
How to find the reason?
How to fix?
Troubleshooting is easier, if we open a URL and get a user-login screen. If login fails, we know we’ve entered the wrong password (or wrong user)
But if we send a token…. How can we know?
We can look at a token:
No matter how long we keep staring at the token: we will never be able to guess what's wrong with it...
What is OAuth 2.0
The OAuth 2.0 specification describes:
There’s a “Resouce Server” which offers data e.g. via a Backend service API
There’s a user who wants to view it
There’s a “client” tool used by the user
There’s an “Authorization Server” which knows the user and the roles of the user
As such, when the user wants to open the resource, the “client” tool delegates the login to the
“Authorization Server”
The “Authorization Server” verifies the login for
- correct user and password (authentication)
- user has required roles (authorization)
To verify that, the server might connect to an Identity provider (depending on landscape)
If everything is fine, then the “Authorization Server” issues an “access token”
The client sends this token to the “Resource Server” in order to access the protected resource
The “Resource Server” knows how to check the token (it might connect to the “Authorization Server” to check validity) and responds with the requested data.
Why is this cool?
Because no involved component has to know the sensitive data like user and password.
The “client” never sees and stores the password, because it delegates to “Authorization Server”
Etc
Only the access token is sent over the net
The access token usually expires quickly, so even if it is stolen, it cannot be reused
What is a token
The OAuth 2.0
specification doesn’t describe how the token should look like (only
how it is used )
It doesn’t describe the format, so it is up to the implementation of “Authorization Server”
The token is a string
The string contains some small pieces of informationThe token type is usually a “Bearer” token, which means that anyone who “carries” it, can access the resource
The token is not encrypted
It is only encoded.
For security reasons, it is signed, and it should be sent with TLS (Transport Layer Security)
Usually, when decoding the string, we get a JSON object
This means that usually, the token format is a “JWT Token”
"JWT" stands for "JSON Web Token"
The “client” doesn’t care about the format, it is just sent to the resource
As for the users, we care even less about it, as we never see it
BUT: only in this blog, we do see it
How can we troubleshoot a JWT token
As we’ve learned above, the token contains the information about user and roles (among other pieces of information)
So, for troubleshooting, we’d like to know this info, such that we can verify if it is correct.
What we want to do is to VIEW the content of the token.
But first we need to get a hold of the token
How to get the token
The token is just a string, easy to copy&paste
If you were following
this tutorial, you’ve seen the token in the browser window.
You can just copy&paste it
If you have an application running in the cloud, e.g. node.js, like described
here or
here, you can add a log output
console.log('Successfully fetched OAuth access token: ' + result.accessToken);
then copy the token from the Cloud Foundry log
Note:
The Cloud Foundry log can be accessed from Cloud Platform Cockpit or via command line (using command:
cf logs <appname> --recent)
Note:
Above snippet is copied from
here
How to view content of the token
It is easy, we just need to decode it.
There are online tools which do the decoding of JWT tokens for us
I’ve tried e.g. this one
https://devtoolzone.com/decoder/jwt
And here’s how the content of a JWT token looks like:
Note:
If you’re wondering about the cryptic abbreviations, used as property names:
These are the so-called “claims” and JWT recommends short names, in order to keep the whole JWT token short
Some claims are registered as default (see
spec), others are specific to e.g. OAuth implementation
How to read JWT token programmatically
If you’re working on an application and you get a hold of the token, you can proceed easier:
Instead of copy&paste the token into a decoder tool, you can decode it yourself, in the code.
So you can directly view the interesting values in the Cloud Foundry log
The following example shows how it is done in node.js
The example is taken from
this tutorial, where we receive the forwarded token in the “Authorization “ header:
var theJwtToken = authHeader.substring(7);// removes 'bearer ' from string
// decode JWT token
var jwtBase64Encoded = theJwtToken.split('.')[1];
var jwtDecoded = Buffer.from(jwtBase64Encoded, 'base64').toString('ascii');
var jwtDecodedJson = JSON.parse(jwtDecoded);
// print useful information: user, scopes, roleCollections
console.log('User: ' + jwtDecodedJson.user_name);
console.log('Scopes: ' + jwtDecodedJson.scope);
console.log('Role Collections of user: ' + jwtDecodedJson['xs.system.attributes']['xs.rolecollections']);
How does this help?
You can check the following information for correctness:
Scope:
If you want to access a Backend service API, the JWT token needs to contain the required scope:
"scope" : [..."Backend-service-..." ... ]
If you don’t see it, then the used XSUAA instance is not properly configured
user_name:
You can check if the user is as expected
xs.rolecollections
You can check if the user has the required role collection assigned
In the JWT token, we can only see the assigned role collections, but not the individual roles contained in the collections
Identity zone
You can check if the target API and the JWT token belong to the same identity zone.
In other words: if both are located in the same subaccount
client_id
Here you can check if the “client” which is registered at “Authorization Server” (in OAuth terms) is the same like the one which you’re using the code dealing with the OAuth flow
To fetch the token, the clientId has to be passed
See here for a code example for handling the OAuth flow
How to continue shooting?
After checking the content of the JWT token, you might need to check the following:
XSUAA params:
If JWT token doesn’t contain the required scope, make sure to create an XSUAA instance with correct parameters.
Binding:
Make sure your app is bound to the correct XSUAA instance. It can easily happen that you bind your app or approuter against a wrong service instance.
I guess it is a good practice to store the XSUAA parameters in a file called xs-security.json, in the application folder (as described
here). Like that you can be sure to use the params required by that app
User Role:
Go to Cloud cockpit to view the role collections mentioned in the JWT token
Is the required role contained in one of them?
The required role is the one defined by Backend service. It has to be assigned to your user (see
here)
Note:
If the error still occurs after fixing the problem:
You might need to logout and login again, or deeply refresh the browser
How do errors look like?
Error Message:
Forbidden
Status code 403
(see
here for definition of 403)
The user doesn’t have the role which is required by Backend service
Error Message:
"You do not have sufficient authorization to access API with name PRODUCTSERVICE..."
The user doesn’t have the role which is required by Backend service
Error Message:
Forbidden
The user doesn’t have the role which is required by App Router
Error Message:
“Authentication object was not found in the SecurityContext”
This error message is sent by Backend service if the required scope is not contained in the JWT token
Error Message:
“Authentication object was not found in the SecurityContext”
Same error message is given by Backend service if no JWT token was received at all
How can I get the Links?
OAuth 2.0 spec:
https://tools.ietf.org/html/rfc6749
OAuth 2.0 Bearer token usage spec
https://tools.ietf.org/html/rfc6750
About JWT:
https://jwt.io/
JWT Spec:
https://tools.ietf.org/html/rfc7519
Example for JWT token decoder tool
https://devtoolzone.com/decoder/jwt
How can I report Errors?
Just post the error and the resolution in the comment section.
Or send to me via personal message
Errors Welcome!