Skip to content

Commit

Permalink
Article: Secure Auth Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
pkozuchowski committed Nov 15, 2024
1 parent 7565fdb commit 4742532
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 1 deletion.
138 changes: 138 additions & 0 deletions docs/SecureAuthProviderCredentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Secure Auth. Provider Secrets
*How to store secrets securely in custom Auth. Providers*


---
# Article

In this article, I will try to explain how Auth. Provider secrets can be stored securely in Salesforce.

## The Problem
When we create and configure custom Auth. Provider, according to Salesforce documentation, we will create a new custom metadata to store all parameters.
Typically, these parameters would like this:
- Authorization Endpoint Url
- Token Endpoint Url
- Consumer Key / Client ID
- Consumer Secret / Client Secret
- Scope

The result of such configuration will be Auth Provider page, which will look like this:
![img_1.png](/img/auth-provider-1.png)

Now, the problem is that all the above fields are text fields - Custom Metadata does not have a concept of secret field.
Any Salesforce Admin or anyone
with `Customize Application` permission can look up Client Id
and Client Secret and use them for malicious purposes. Even worse if that person leaves the company knowing these secrets. There's no telling who knows these
secrets.

## Solution
To circumvent this situation, we will move these secrets to different storage: External Credential.

### External Credential
First, we will create an External Credential with Custom authentication protocol. This will be our safe storage for secrets - what's stored here is not
visible to anyone.
![img_2.png](/img/auth-provider-2.png)

### Note!
External Credential Principal has to be added in Permission Set or Profile to use it.

### Named Credential
Secondly, we will create a Named Credential connected to External Credential. It is essential to check `Allow Formulas in HTTP Body`. We don't
need `Generate Authorization Header` though.
![img_3.png](/img/auth-provider-3.png)

### Auth. Provider
Thirdly, we will change Auth. Provider to use Named Credential. Secrets use special merge field that will be replaced by Named
Credential - `{!$Credential.Microsoft.ClientSecret}`.
- `.Microsoft.` is Developer Name of External Credential. It has to be External Credential linked to our Named Credential
- `.ClientSecret` is the name Authentication Parameter on our Principal
![img_4.png](/img/auth-provider-4.png)
*Note: I left Authorization Endpoint Url, because it is returned to the browser. *

And we are done!

Our Http Request body should now look like this:
```
code=XXX
&grant_type=authorization_code
&redirect_uri={{redirectURI
&client_id=c8cd6366-bdcc-4f25-8370-532a52054c76
&client_secret={!$Credential.Microsoft.ClientSecret}
```

## Deployment Tips
At last, just a few pointers for deploying Auth. Providers. This type of metadata usually has different configurations on different sandboxes, so it requires
some flexibility.


#### 1. Don't deploy fields you don't want to overwrite
If you don't want to have your values overwritten on deployment, just remove them from the repository. You can deploy blank Auth. Provider to org, fill the
details once and never touch it again. Or don't include metadata record at all.

The tip inside a tip here is to not make any of the fields required. Just save yourself trouble.

No substitutions, no CI/CD environments, no trouble.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<label>MyAuthProvider</label>
<protected>false</protected>
<values>
<field>Authorization_Endpoint_Url__c</field>
<value xsi:type="xsd:string">https://login.microsoftonline.com/693ad9ee-7efd-43d0-a0ab-2fd666808a42/oauth2/v2.0/authorize</value>
</values>
<!--These fields will not be overwritten-->
<!-- <values>-->
<!-- <field>Client_Id__c</field>-->
<!-- <value xsi:type="xsd:string">cfd291af-99e7-48b9-abd1-7616368b2a1c</value>-->
<!-- </values>-->
<!-- <values>-->
<!-- <field>Client_Secret__c</field>-->
<!-- <value xsi:type="xsd:string">{!$Credential.Microsoft.ClientSecret}</value>-->
<!-- </values>-->
<values>
<field>Scope__c</field>
<value xsi:type="xsd:string">openid offline_access</value>
</values>
<values>
<field>Token_Endpoint_Url__c</field>
<value xsi:type="xsd:string">callout:MicrosoftLogin/693ad9ee-7efd-43d0-a0ab-2fd666808a42/oauth2/v2.0/token</value>
</values>
</CustomMetadata>
```

#### 2. SFDX Substitutions
You may find it useful to use substitutions, which are now inbuilt in SF CLI. In that case, you set environment variable in CI/CD runner for each environment.

In our example, we can set `AAD_TENANT_ID` as global variable in GitHub and have it replaced in Auth. Provider metadata:

```xml

<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<values>
<field>Authorization_Endpoint_Url__c</field>
<value xsi:type="xsd:string">https://login.microsoftonline.com/AAD_TENANT_ID/oauth2/v2.0/authorize</value>
</values>
<values>
<field>Token_Endpoint_Url__c</field>
<value xsi:type="xsd:string">callout:MicrosoftLogin/AAD_TENANT_ID/oauth2/v2.0/token</value>
</values>
</CustomMetadata>
```

sfdx-project.json:
```json
{
"replacements": [
{
"filename": "force-app/main/default/customMetadata/MicrosoftAuthProvider.MyAuthProvider.md-meta.xml",
"stringToReplace": "AAD_TENANT_ID",
"replaceWithEnv": "AAD_TENANT_ID"
}
]
}
```

[Link to Documentation](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_string_replace.htm)
Binary file added docs/img/auth-provider-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/auth-provider-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/auth-provider-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/auth-provider-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><meta name="description" content="Open source library for Salesforce. Salesforce Frameworks, Accelerators, Components, Utilities."><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/cloud.svg"><link rel="stylesheet" href="/salesforce-lightning-design-system.min.css"><title>Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities</title><script type="text/javascript">!function(n){if("/"===n.search[1]){var a=n.search.slice(1).split("&").map((function(n){return n.replace(/~and~/g,"&")})).join("?");window.history.replaceState(null,null,n.pathname.slice(0,-1)+a+n.hash)}}(window.location)</script><script defer="defer" src="/static/js/main.2d530ebc.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"><script defer="defer" src="/static/js/main.fa12e836.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div style="display:none"><h1>Apex Commons</h1><h1>Salesforce Frameworks</h1><h1>Salesforce Accelerators</h1><h2>Trigger Handler</h2><h2>Lambda</h2><h2>Collection</h2><h2>XML</h2><h2>Selectors</h2><h2>Selector Layer</h2><h1>LWC</h1></div><div id="root" class="fullHeight"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><meta name="description" content="Open source library for Salesforce. Salesforce Frameworks, Accelerators, Components, Utilities."><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/cloud.svg"><link rel="stylesheet" href="/salesforce-lightning-design-system.min.css"><title>Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities</title><script type="text/javascript">!function(n){if("/"===n.search[1]){var a=n.search.slice(1).split("&").map((function(n){return n.replace(/~and~/g,"&")})).join("?");window.history.replaceState(null,null,n.pathname.slice(0,-1)+a+n.hash)}}(window.location)</script><script defer="defer" src="/static/js/main.2d530ebc.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"><script defer="defer" src="/static/js/main.fa12e836.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"><script defer="defer" src="/static/js/main.ca2a4e68.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div style="display:none"><h1>Apex Commons</h1><h1>Salesforce Frameworks</h1><h1>Salesforce Accelerators</h1><h2>Trigger Handler</h2><h2>Lambda</h2><h2>Collection</h2><h2>XML</h2><h2>Selectors</h2><h2>Selector Layer</h2><h1>LWC</h1></div><div id="root" class="fullHeight"></div></body></html>
2 changes: 2 additions & 0 deletions docs/static/js/main.ca2a4e68.js

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions docs/static/js/main.ca2a4e68.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/

/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/

/*!
Copyright (c) 2015 Jed Watson.
Based on code that is Copyright 2013-2015, Facebook, Inc.
All rights reserved.
*/

/*!
* Adapted from jQuery UI core
*
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/

/*!
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/

/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @remix-run/router v1.7.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/

/**
* React Router DOM v6.14.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/**
* React Router v6.14.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

0 comments on commit 4742532

Please sign in to comment.