How to call protected app | from external app || as external user ||| with scope
Quicklinks:
Quick Guide
Sample Code
This is the project structure for our tutorial
{
"xsappname" : "xsappforproviderapp",
"tenant-mode" : "dedicated",
"scopes": [{
"name": "$XSAPPNAME.scopeforproviderapp",
"granted-apps" : [ "$XSAPPNAME(application,xsappforhumanuser)"],
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, 123-123, xsappforclientapp)"]
}]
}
{
"xsappname" : "xsappforclientapp",
"tenant-mode" : "dedicated",
"authorities":["$ACCEPT_GRANTED_AUTHORITIES"]
}
"scopes": [{
"grant-as-authority-to-apps" : [
"$XSAPPNAME(application, 12344-abc, xsappforclientapp)"
"authorities":["$ACCEPT_GRANTED_AUTHORITIES"]
{
"xsappname" : "xsappforproviderapp",
"tenant-mode" : "dedicated",
"scopes": [{
"name": "$XSAPPNAME.scopeforproviderapp",
"granted-apps" : [ "$XSAPPNAME(application,xsappforhumanuser)"],
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, 123-abc, xsappforclientapp)"]
}]
}
{
"main": "server.js",
"dependencies": {
"@sap/xsenv": "latest",
"@sap/xssec": "latest",
"express": "^4.16.3",
"passport": "^0.4.1"
}
}
const express = require('express');
const passport = require('passport');
const xsenv = require('@sap/xsenv');
const JWTStrategy = require('@sap/xssec').JWTStrategy;
//configure passport
const xsuaaService = xsenv.getServices({ myXsuaa: { tag: 'xsuaa' }});
const xsuaaCredentials = xsuaaService.myXsuaa;
const jwtStrategy = new JWTStrategy(xsuaaCredentials)
// configure express server with authentication middleware
passport.use(jwtStrategy);
const app = express();
// Middleware to read JWT sent by client
function jwtLogger(req, res, next) {
console.log('===> Decoding auth header' )
const jwtToken = readJwt(req)
if(jwtToken){
console.log('===> JWT: audiences: ' + jwtToken.aud);
console.log('===> JWT: scopes: ' + jwtToken.scope);
console.log('===> JWT: client_id: ' + jwtToken.client_id);
}
next()
}
app.use(jwtLogger)
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));
// app endpoint with authorization check
app.get('/getData', function(req, res){
console.log('===> Endpoint has been reached. Now checking authorization')
const MY_SCOPE = xsuaaCredentials.xsappname + '.scopeforproviderapp'// scope name copied from xs-security.json
if(req.authInfo.checkScope(MY_SCOPE)){
res.send('The endpoint was properly called, role available, delivering data');
}else{
const jwtToken = readJwt(req)
const availableScopes = jwtToken ? jwtToken.scope : {}
return res.status(403).json({
error: 'Unauthorized',
message: `Missing required role: <scopeforproviderapp>. Available scopes: ${availableScopes}`
});
}
});
const readJwt = function(req){
const authHeader = req.headers.authorization;
if (authHeader){
const theJwtToken = authHeader.substring(7);
if(theJwtToken){
const jwtBase64Encoded = theJwtToken.split('.')[1];
if(jwtBase64Encoded){
const jwtDecoded = Buffer.from(jwtBase64Encoded, 'base64').toString('ascii');
return JSON.parse(jwtDecoded);
}
}
}
}
// start server
app.listen(process.env.PORT || 8080, () => {
console.log('Server running...')
})
---
applications:
- name: providerapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforprovider
env:
DEBUG: xssec:*
{
"xsappname" : "xsappforclientapp",
"tenant-mode" : "dedicated",
"authorities":["$XSAPPNAME(application,xsappforproviderapp).scopeforproviderapp"]
}
{
"dependencies": {
"express": "^4.16.3"
}
}
const express = require('express')
const app = express()
const https = require('https');
// access credentials from environment variable (alternatively use xsenv)
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES)
const CREDENTIALS = VCAP_SERVICES.xsuaa[0].credentials
//oauth
const OA_CLIENTID = CREDENTIALS.clientid;
const OA_SECRET = CREDENTIALS.clientsecret;
const OA_ENDPOINT = CREDENTIALS.url;
// endpoint of our client app
app.get('/trigger', function(req, res){
doCallEndpoint()
.then(()=>{
res.status(202).send('Successfully called remote endpoint.');
}).catch((error)=>{
console.log('Error occurred while calling REST endpoint ' + error)
res.status(500).send('Error while calling remote endpoint.');
})
});
// helper method to call the endpoint
const doCallEndpoint = function(){
return new Promise((resolve, reject) => {
return fetchJwtToken()
.then((jwtToken) => {
const options = {
host: 'providerapp.cfapps.eu10.hana.ondemand.com',
path: '/getData',
method: 'GET',
headers: {
Authorization: 'Bearer ' + jwtToken
}
}
const req = https.request(options, (res) => {
res.setEncoding('utf8')
const status = res.statusCode
if (status !== 200 && status !== 201) {
return reject(new Error(`Failed to call endpoint. Error: ${status} - ${res.statusMessage}`))
}
res.on('data', () => {
resolve()
})
});
req.on('error', (error) => {
return reject({error: error})
});
req.write('done')
req.end()
})
.catch((error) => {
reject(error)
})
})
}
// jwt token required for calling REST api
const fetchJwtToken = function() {
return new Promise ((resolve, reject) => {
const options = {
host: OA_ENDPOINT.replace('https://', ''),
path: '/oauth/token?grant_type=client_credentials&response_type=token',
headers: {
Authorization: "Basic " + Buffer.from(OA_CLIENTID + ':' + OA_SECRET).toString("base64")
}
}
https.get(options, res => {
res.setEncoding('utf8')
let response = ''
res.on('data', chunk => {
response += chunk
})
res.on('end', () => {
try {
const jwtToken = JSON.parse(response).access_token
resolve(jwtToken)
} catch (error) {
return reject(new Error('Error while fetching JWT token'))
}
})
})
.on("error", (error) => {
return reject({error: error})
});
})
}
// Start server
app.listen(process.env.PORT || 8080, ()=>{})
---
applications:
- name: clientapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforclient
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
34 | |
14 | |
13 | |
13 | |
11 | |
11 | |
9 | |
8 | |
7 | |
7 |