
Before I turn to the concrete scenario and implementation, I will first take a closer look at the topic of OAuth. Most people in the IT environment have come across this term before, and a majority is still able to provide information when it comes to explaining at a high-level what it is and what it is used for. But as soon as it comes to the technical details, the exact workings of the protocols, the different authentication flows, etc., many (including me) quickly get tripped up. But fortunately I'm not alone in providing the most important information about OAuth, because there are also a large number of wonderful people who have a very deep understanding of the subject and are willing to document this knowledge and make it digitally available to their fellow world in an easy to understand way. One resource I came across a couple of years ago that has helped sharpen my understanding of this topic is this talk by Nate Barbettini: https://www.youtube.com/watch?v=996OiexHze0 I can highly recommend this video to anyone who would like to learn more about OAuth 2.0 (and for good measure, OIDC) or is looking for a simple yet solid introduction to the topic. The investment of this one hour is definitely worth it.
For those who are in a hurry, here is the most important information in a nutshell:
OAuth 2.0 is an open protocol that allows standardized, secure API authorization for desktop, web and mobile applications. This also covers the scenario of access delegation, i.e. that an application gets access to certain data or functionalities of another application with the permission of the user. Classic example: an application is granted permission by a user to send posts on Facebook or read contact data on their behalf.
Resource Owner - The owner of a resource (data). As a rule, this is simply a user, i.e. the person sitting in front of the computer / smartphone / tablet / ...
Resource Server - This is the application that stores the resources (data) of the resource owner (user)
Client - The client is the third-party application that wants to access resources (data) of the resource server on behalf of the resource owner (user) and needs a token for authentication that contains the corresponding authorization grant (so to speak, the proof that the client has received permission from the resource owner to access the resource server)
Authorization Server - The Authorization Server is the instance that manages Authorization grants and issues the tokens (these are usually JWT (JSON Web Tokens), i.e. JSON objects that contain the relevant token information) that authorize the client to access the Resource Server on behalf of the Resource Owner. In some cases, Resource Server and Authorization Server are identical, but often they are separate instances
The XSUAA service, inside of the SAP BTP, handles the authorization flow between users, identity providers, and the applications or services. The XSUAA service is an internal development from SAP dedicated for the SAP BTP. In the Cloud Foundry project, there is an open-source component called UAA. UAA is an OAuth provider which takes care of authentication and authorization. SAP used the base of UAA and extended it with SAP specific features to be used in SAP BTP.
At first, I will show you the basics of the implementation of the server app.
To be able to secure any endpoint of this service via XSUAA, we need to establish a binding between server app and XSUAA. This is done in the mta.yaml file that we use for the server app deployment:
...
resources:
...
- name: xsuaa-server-service
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
service-name: xsuaa-server-service
{
"requires": {
"auth": {
"kind": "jwt-auth"
},
"uaa": {
"kind": "xsuaa"
}
}
}
service ServerService @(requires: 'authenticated-user') {
function helloWorldServer() returns String;
}
module.exports = async (srv) => {
srv.on('helloWorldServer', async (req) => {
return 'Hello World from Server'
})
}
srv.on('helloWorldClientCloudSDK', async () => {
return executeHttpRequest({
destinationName: 'Server'
}).then((response) => response.data)
})
srv.on('helloWorldClientPlain', async () => {
// get all the necessary destination service parameters from the
// service binding in the VCAP_SERVICES env variable
const vcapServices = JSON.parse(process.env.VCAP_SERVICES)
const destinationServiceUrl =
vcapServices.destination[0].credentials.uri +
'/destination-configuration/v1/destinations/'
const destinationServiceClientId =
vcapServices.destination[0].credentials.clientid
const destinationServiceClientSecret =
vcapServices.destination[0].credentials.clientsecret
const destinationServiceTokenUrl =
vcapServices.destination[0].credentials.url +
'/oauth/token?grant_type=client_credentials'
// before we can fetch the destination from the destination
// service, we need to retrieve an auth token
const token = await axios.post(
destinationServiceTokenUrl,
null,
{
headers: {
authorization: 'Basic ' +
Buffer.from(
destinationServiceClientId +
':' +
destinationServiceClientSecret'
).toString('base64'),
},
}
)
const destinationServiceToken = token.data.access_token
// with this token, we can now request the "Server" destination
// from the destination service
const headers = {
authorization: 'Bearer ' + destinationServiceToken,
}
const destinationResult = await axios.get(
destinationServiceUrl + 'Server',
{ headers }
)
const destination = destinationResult.data
// now, we use the retrieved the destination information to send
// a HTTP request to the token service endpoint;
// to authenticate, we take the Client ID attribute and the
// Client Secret attribute from the destination,
// encode ClientId:ClientSecret to Base64 and send the resulting
// string prefixed with "Basic " as Authorization
// header of the request;
// as a response, we receive a JWT token that we can then use to
// authenticate against the server
// alternatively, the JWT token could directly be fetched from
// destination.authTokens[0].value
const jwtTokenResponse = await axios.get(
destination.destinationConfiguration.tokenServiceURL +
'?grant_type=client_credentials',
{
headers: {
Authorization: 'Basic ' +
btoa(destination.destinationConfiguration.clientId +
':' +
destination.destinationConfiguration.clientSecret),
}
}
)
const jwtToken = jwtTokenResponse.data.access_token
// here we call the server instance with the bearer token we
// received from the token service endpoint
const destinationResponse = await axios.get(
destination.destinationConfiguration.URL,
{
headers: {
Authorization: 'Bearer ' + jwtToken,
},
}
)
return destinationResponse.data
})
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
7 | |
7 | |
6 | |
4 | |
4 | |
4 | |
4 | |
4 | |
4 |