Skip to main content

Why introducing AWS Lambda function

 

The main reason the integration is leveraging a Lamdba function is to overcome the lack of support in Okta for OAuth client credentials.

 

Source:

https://developer.okta.com/docs/concepts/scim/faqs/

 

Disclaimer

The Endpoint Credential Manager (ECM) Software Development Kit

Allows developers to create Custom ECM Plugins. The SDK comes with a Plugin example, which has been used as a starting point to create a new Plugin.

Any sample or proof of concept code (“Code”) provided on the Community is provided “as is” and without any express or implied warranties. This means that we do not promise that it will work for your specific needs or that it is error-free. Such Code is community supported and not supported directly by BeyondTrust, and it is not intended to be used in a production environment. BeyondTrust and its contributors are not liable for any damage you or others might experience from using the Code, including but not limited to, loss of data, loss of profits, or any interruptions to your business, no matter what the cause is, even if advised of the possibility of such damage.

 

Q: How should I be managing authentication to my SCIM API?

Okta recommends using the OAuth 2.0 Authorization Code grant flow. Okta doesn't support the Client Credentials or Resource Owner Password Credentials Authorization grant flows for SCIM. The Authorization Code grant flow is more common in SaaS and cloud integrations and is also more secure.

Comment: Okta is stating that Authorization Code grant flow is more secure, but it means that when Okta SCIM App needs to authenticate to Password Safe, it would actually be Password Safe that would have to authenticate back to Okta, using Client ID and Client Secret generated by Okta. This translate into PAM trusting the Directory, when it should be the other around, from a Security Best Practices point of view.

 

Another reason is to enable the support for Okta Group Push.  Password Safe requires a description attribute at creation time, and Okta only provides a displayName attribute, which makes the Okta Push provisioning request to fail, being an invalid request missing the mandatory attribute description.

 

AWS Lambda function

Prerequisites and configuration

 

import axios from 'axios';

export const handler = async (event) => {
// ####### Extract host and endpoint from Request Url ####################
console.log('event INCOMING = '+JSON.stringify(event));
const method = event.requestContext.http.method;
console.log('event method = '+method);
const endpoint = 'https:/'+event.rawPath;
let url = '';
if(event.rawQueryString){
url = endpoint+'?'+event.rawQueryString;
} else{
url = endpoint;
}
var host = event.rawPath.substring(1);
host = host.substring(0,host.indexOf('/'));
console.log('host = '+host+' endpoint = '+endpoint);

// ####### Extract values from base64 token ####################
const base64token = event.headers.authorization.substring(7);
const base64payload = Buffer.from(base64token, 'base64').toString('ascii');
console.log('base64token = '+base64token+'. base64payload = '+base64payload);
const base64obj = JSON.parse(base64payload);
const authUrl = base64obj.authURL;
const client_id = base64obj.client_id;
const client_secret = base64obj.client_secret;
const target = base64obj.target;
console.log('target = '+target+' authUrl = '+authUrl+'. client_id = '+client_id+' client_secret = '+client_secret);
// Change Header host value for gateway
event.headers.host = host;

// ####### Authenticate ####################
const data = 'grant_type=client_credentials';
const authorization = 'Basic ' + Buffer.from(client_id+':'+client_secret).toString('base64');
const authConfig = {
method: 'post',
rejectUnauthorized: false,
url: authUrl,
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
console.log('authConfig = '+JSON.stringify(authConfig));
let authRes = await axios(authConfig);
console.log('authRes = '+JSON.stringify(authRes.data));
const access_token = authRes.data.access_token;

// ####### GET/POST/PATCH/PUT/DELETE data from target #########
If (target == "PasswordSafe" && endpoint == 'https://'+host+'/scim/v2/Groups' && method == 'POST'){
console.log('We have a Password Safe Create Group call body : '+JSON.stringify(event.body));
const body = JSON.parse(event.body);
const configG = {
method: method,
url: url,
headers: {
'content-type': event.headers-'content-type'],
accept: event.headers.accept,
authorization: 'Bearer '+access_token,
},
"data": {
"schemas": /
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName": body.displayName,
"description": body.displayName,
"members": /]
}
};
console.log('Password Safe Create Group: configG = '+JSON.stringify(configG));
let resG = await axios(configG);

// Response - For testing only
resG.headersb'content-type'] = 'application/scim+json';
console.log('axios res data = '+JSON.stringify(resG.data));
console.log('axios res headers = '+JSON.stringify(resG.headers));
const response = {
statusCode: 200,
body: JSON.stringify(resG.data),
headers: resG.headers
};
return response;

} else{
if(event.headers 'content-type']) {'Content-Type = '+console.log(event.headerso'content-type']);}
let config = {};
if(event.body) {
console.log('body = '+JSON.stringify(event.body));
config = {
method: method,
url: url,
headers: {
'content-type': event.headers-'content-type'],
accept: event.headers.accept,
authorization: 'Bearer '+access_token,
},
data: JSON.parse(event.body)
};
} else {
config = {
method: method,
url: url,
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer '+access_token
}
};
}
console.log('axios config = '+JSON.stringify(config));
let res = await axios(config);

// Response – For Okta
res.headerst'content-type'] = 'application/scim+json';
console.log('axios res data = '+JSON.stringify(res.data));
console.log('axios res headers = '+JSON.stringify(res.headers));
const response = {
statusCode: 200,
body: JSON.stringify(res.data),
headers: res.headers
};
return response;
}
};

 

 

AWS Lambda function
The Lambda function code above includes multiple console.log() items to facilitate troubleshooting via AWS CloudWatch

 

We must configure a Function Url with authentication set to NONE

 

Before configuration Okta SCIM App

SCIM 2.0 Base Url and OAuth Bearer Token

 

The SCIM 2.0 Base Url is the Function URL with the Password Safe Url (minus https://) appended to it, for example:

 

https://7zevosnxabcdwul77yt1234567a.lambda-url.us-east-1.on.aws/pws_instance.ps.beyondtrustcloud.com/scim/v2

 

We need to create a SCIM Service Account for Okta in Password Safe, and add it to a Group with the following Features and Smart Group permissions:

 

Read only permissions to all Managed Account Smart Groups is required.

 

Only the Managed Account Smart Group with also the Category defined as Managed Account are visible via SCIM

 

To create the Bearer Token for Okta, we must first create a JSON document:

 

{"target":"PasswordSafe","authURL":"https://pws_instance.ps.beyondtrustcloud.com/scim/oauth/token","client_id":"b0d00123456abcde4a7611b9","client_secret":"63eee16d6b3456789012vfdes1bc5e6"}

 

Then we need to create a base64 encoded version, e.g. by using https://base64encode.org :

 

eyJ0YXJnZXQiOiJQYXNzd29yZFNhZmUiLCJhdXRoVVJMIjoiaHR0cHM6Ly9wd3NfaW5zdGFuY2UucHMuYmV5b25kdHJ1c3RjbG91ZC5jb20vc2NpbS9vYXV0aC90b2tlbiIsImNsaWVudF9pZCI6ImIwZDAwMTIzNDU2YWJjZGU0YTc2MTFiOSIsImNsaWVudF9zZWNyZXQiOiI2M2VlZTE2ZDZiMzQ1Njc4OTAxMnZmZGVzMWJjNWU2In0=

 

Configure Okta SCIM App

SCIM 2.0 Base Url and OAuth Bearer Token

 

Under Applications, select Browse Catalog, and select SCIM 2.0 Test App (OAuth Bearer Token).

 

Under Provisioning/Integration, provide the Url and token

 

Under Settings/To App, check all the boxes and select the Password Sync option

 

We will have to modify the Password Policy under Security|Authentication to match the Password Safe password policy for Users
Under Sign On, select Email prefix for Username

 

We need to modify the Profile Mapping for the App

 

We need to modify the Profile Mapping for the App

 

We need to modify the Profile Mapping for the App

 

We need to modify the Profile Mapping for the App

 

Supported Use Cases

Overview

 

Import and Create in Okta

 

Push Group

 

Assign Okta Users (Create in Password Safe)

 

Import Password Safe Groups and add/remove members that exist on both sides

 

Push Group example

 

Push Group example

 

 

Be the first to reply!

Reply