Skip to content

Commit

Permalink
Add SSO Plugin
Browse files Browse the repository at this point in the history
This commit adds the first version of the SSO Plugin, including:

- A settings control for app owners to set the Auth0 API credentials
  (domain and client ID).
- A plugin widget for negotiating with the Auth0 API, determining
  authentication status, offering the user the ability to login via
  popup, saving the resulting user object to local storage, and
  instructing the user to go to the RSS plugin.

It does not yet automatically forward the user to the RSS plugin
automatically. Also, the plugin.json file may need further tweaking as
we understand its intended contents.

Affects #3
  • Loading branch information
reefdog committed Jun 24, 2020
1 parent 637dfac commit 1fe8cea
Show file tree
Hide file tree
Showing 3 changed files with 501 additions and 0 deletions.
199 changes: 199 additions & 0 deletions ssoPlugin/control/settings/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- The BuildFire JS library is required. -->
<script src="../../../../scripts/buildfire.min.js"></script>
<style type="text/css">
#bif-sso__wrapper {
padding: 1em;
}

.form-control-group {
margin: 0.5em 0;
}
.form-control-group label {
font-weight: bold;
display: inline-block;
text-align: right;
width: 7em;
margin-right: 0.25em
}
.form-control-group.align-with-labels {
padding-left: 7.25em;
}

form.disabled {
opacity: 0.5;
cursor: not-allowed;
}

.bif-sso__auth-api-settings__alert-message--success {
color: #00b500;
}
.bif-sso__auth-api-settings__alert-message--error {
color: #ec0000;
}
</style>
</head>
<body>

<div id="bif-sso__wrapper">
<form id="bif-sso__auth-api-settings">
<fieldset>
<legend>Auth0 API Settings</legend>
<div class="form-control-group">
<label for="bif-sso__auth-api-settings__domain">Domain</label>
<input id="bif-sso__auth-api-settings__domain" type="text">
</div>
<div class="form-control-group">
<label for="bif-sso__auth-api-settings__client-id">Client ID</label>
<input id="bif-sso__auth-api-settings__client-id" type="text">
</div>
<div class="form-control-group align-with-labels">
<button type="submit">Save settings</button>
<span id="bif-sso__auth-api-settings__alert-message"></span>
</div>
</fieldset>
</form>
</div>

<script type="text/javascript">
// ============================================================================================
// UTILITY METHODS
// ============================================================================================

/**
* Runs the provided function when the document is fully loaded.
*
* @param {Function} fn The function to be run.
*/
const ready = (fn) => {
if (document.readyState != 'loading') fn()
else document.addEventListener('DOMContentLoaded', fn)
}

/**
* Enables or disables the provided form element, depending on its current state.
*
* In this case we disable a form by adding the "disabled" class and setting the `disabled`
* property to `true` on all its child form elements, and enable by doing the opposite.
*
* @param {HTMLElement} $form The form element to toggle.
*/
const toggleForm = ($form) => {
const $formElements = $form.querySelectorAll('input, textarea, select, button')
if ($form.classList.contains('disabled')) {
$form.classList.remove('disabled')
$formElements.forEach(($el) => $el.disabled = false)
} else {
$form.classList.add('disabled')
$formElements.forEach(($el) => $el.disabled = true)
}
}

// ============================================================================================
// APPLICATION METHODS
// ============================================================================================

// Set DOM variables that we'll reference in the following methods thanks to lexical scoping.
const $apiSettingsForm = document.getElementById('bif-sso__auth-api-settings')
const $apiSettingsFieldDomain = document.getElementById('bif-sso__auth-api-settings__domain')
const $apiSettingsFieldClientId = document.getElementById('bif-sso__auth-api-settings__client-id')
const $apiSettingsAlertMessage = document.getElementById('bif-sso__auth-api-settings__alert-message')

/**
* Updates the alert message placeholder with the provided message and type.
*
* Expected types are "error" or "success". Default is "notification" and has no special style.
*
* @param {String} message The message that should be displayed.
* @param {String} messageType? The message type, from the list above.
*/
const alertMessage = (message, messageType = 'notification') => {
const filteredClassNames = $apiSettingsAlertMessage.className
.split(' ')
.filter((className) => !className.startsWith('bif-sso__auth-api-settings__alert-message--'))
if (message) filteredClassNames.push(`bif-sso__auth-api-settings__alert-message--${messageType}`)
$apiSettingsAlertMessage.className = filteredClassNames.join(' ').trim()
$apiSettingsAlertMessage.innerHTML = message
}

/**
* Validates that the required settings exist and aren't falsey.
*
* @param {Object} settings An object containing the settings.
*/
const areValidSettings = (settings) => {
return settings
&& settings.authApi
&& settings.authApi.domain
&& settings.authApi.clientId
}

/**
* Prefills the settings form with the provided values, if they're set.
*
* @param {Object} settings An object containing the settings.
*/
const prefillApiSettingsForm = (settings) => {
$apiSettingsFieldDomain.value = settings.domain || ''
$apiSettingsFieldClientId.value = settings.clientId || ''
}

/**
* Saves the settings to the remote BuildFire datastore.
*
* TODO: Make this a Promise, probably.
*
* @param {Object} settings An object containing the settings.
*/
const saveSettings = (settings) => {
toggleForm($apiSettingsForm)
buildfire.datastore.save(settings, 'settings', (err, status) => {
toggleForm($apiSettingsForm)
if (err || !status) alertMessage('❌ Unable to save settings', 'error')
else alertMessage('✅ Settings saved!', 'success')
})
}

/**
* Handler for the form submission that saves to the BuildFire datastore.
*
* @param {SubmitEvent} e The native submit event fired by the browser.
*/
const handleSettingsFormSubmit = (e) => {
// Stop the native form submission
e.preventDefault()

// Clear the alert message
alertMessage('')

const settings = {
authApi: {
domain: $apiSettingsFieldDomain.value,
clientId: $apiSettingsFieldClientId.value,
},
}

if (areValidSettings(settings)) saveSettings(settings)
else alertMessage('❌ Please provide a valid domain and client ID.', 'error')
}

// ============================================================================================
// INIT/START
// ============================================================================================

ready(() => {
$apiSettingsForm.addEventListener('submit', handleSettingsFormSubmit)

// Fetch and prefill the existing auth API settings.
buildfire.datastore.get('settings', (err, data) => {
// An error here means interacting with BuildFire failed, not just that data doesn't exist.
if (err) alertMessage('Error fetching existing API settings from BuildFire.', 'error')
else if (data.data && data.data.authApi) prefillApiSettingsForm(data.data.authApi)
})
})
</script>
</body>
</html>
21 changes: 21 additions & 0 deletions ssoPlugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"author": "Bad Idea Factory",
"pluginName": "SSO Management",
"pluginDescription": "Allow users to sign into your application.",
"supportEmail": "[email protected]",
"control": {
"content": {
"enabled": false
},
"design": {
"enabled": false
},
"settings": {
"enabled": true
}
},
"widget": {
},
"features": [],
"languages": ["en"]
}
Loading

0 comments on commit 1fe8cea

Please sign in to comment.