not getting any token
I am using this codeĀ
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<style id="Bootstrap5-overrides">
/*Bootstrap 5 _utilities.scss mixin override */
.logi-embed .zdView-Widget[widget-mode-size]:hover .widgetHeader .widget-controls.invisible,
.logi-embed .zdView-Widget[widget-mode-size].dragging .widgetHeader .widget-controls.invisible,
.logi-embed .zdView-Widget[widget-mode-size].active-dragging .widgetHeader .widget-controls.invisible,
.logi-embed .zdView-Widget[widget-mode-size].active-resizing .widgetHeader .widget-controls.invisible {
display: flex;
visibility: visible!important;
}
</style>
<!-- 2021-09-03 GM: updated src path to point to local:8080 **Update if your Composer server is installed elsewhere -->
<script data-name="logi-embed-manager"
src="http://localhost:8080/composer/embed/embed.js"></script>
<script>
//2021-09-03 GM: Updated globals below to match local docker trial. **Update as necessary if your server is different
//globals - defaults
const ComposerUrl = 'http://localhost:8080/composer';
const SupervisorCreds = window.btoa('supervisor:Password1!');
const ClientName = 'ClientExample';
const AdminUsername = 'admin';
const EndUsername = 'admin';
const EndUserGroup = 'Administrators';
const componentConfig = {
"theme":"modern",
"interactivityProfileName":"interactive",
// START: Interactivity profile overrides
// The interactiveProfileName property must be specified before
// the interactivity overrides (interactivityOverrides object)
// can work. The interactivityOverrides object properties affect
// the current embedded dashboard properties. The visualSettings
// object properties affect all visuals properties. Please read the
// Documentation Helper for more properties that can be overridden.
"interactivityOverrides": {
"name": "interactive",
"type": "SYSTEM",
"overrideVisualInteractivity": true,
"settings": {
"REFRESH": true,
"CHANGE_LAYOUT": true,
"RENAME": true,
"SHARE_FILTER_SETS": true,
"DASHBOARD_INTERACTIONS": true,
"ADD_TO_FAVORITES": true,
"DELETE": true,
"FILTER": true,
"EXPORT_PNG_PDF": true,
"ADD_VISUALS": true,
"EXPORT_CONFIGURATION": true,
"DASHBOARD_LINKS": true,
"SAVE": true
},
"visualSettings": {
"ACTIONS_ACTION": true,
"RULERS": true,
"ZOOM_ACTION": true,
"FILTER_ACTION": true,
"COLORS": true,
"METRICS": true,
"ACTIONS": true,
"TREND_ACTION": true,
"VISUAL_STYLE": true,
"KEYSET_ACTION": true,
"KEYSET": undefined,
"COPY": true,
"SETTINGS": true,
"EXPORT": true,
"TIMEBAR_PANEL": true,
"DETAILS_ACTION": true,
"MAXIMIZE": true,
"LINK_ACTION": true,
"FILTER": true,
"REMOVE": true,
"GROUPING": true,
"RENAME": true,
"SORT": true,
"TIMEBAR_FIELD": true
}
},
/* END: interactivity profile overrides */
"editor": { "placement": "dockRight" },
"header": {
"showTitle": false,
}
}
</script>
<script>
// - set by functions to reuse
Client = {};
ClientCredsForAccessTokenGeneration = {};
AdminAccessToken = {};
AdminGroups = {};
EndUser = {};
EndUserAccessToken = {};
EndUserDashboards = {};
msgToggle = true; //true: show - false: hide
/* Helper functions to talk to Composer and present info on page ---------------------- */
const Get = async (GetUrl, AccessToken, AuthType = 'Bearer') => {
return await fetch(GetUrl, {
headers: {
'Origin': 'http://localhost',
'Access-Control-Request-Method': 'GET',
'Access-Control-Request-Headers': 'Authorization, Content-Type',
'Content-Type': 'application/vnd.composer.v3+json',
'Authorization': `${AuthType} ${AccessToken}`,
},
}).then(x => x.json());
};
const Post = async (ReqUrl, AuthCreds, AuthType = 'Basic', ReqBody) => {
return await fetch(ReqUrl, {
method: 'POST',
headers: {
'Accept': 'application/vnd.composer.v3+json',
'Origin': 'http://localhost',
'Access-Control-Request-Method': 'GET',
'Access-Control-Request-Headers': 'Authorization, Content-Type',
'Content-Type': 'application/vnd.composer.v3+json',
'Authorization': `${AuthType} ${AuthCreds}`,
},
body: JSON.stringify(ReqBody)
}).then(x => x.json());
};
const Del = async (GetUrl, AuthCreds, AuthType = 'Basic') => {
return await fetch(GetUrl, {
method: 'DELETE',
headers: {
'Accept': '*/*',
'Content-Type': 'application/vnd.composer.v2+json',
'Authorization': `${AuthType} ${AuthCreds}`
}
});
};
const logMessage = (message) => {
let messageContainer = document.getElementById('messagebox');
if (messageContainer == null) {
document.getElementById("message").innerHTML = '<div id="messagebox" style="max-height: 300px; overflow: auto;" class="alert alert-success">' + message + '<BR></div>';
} else {
messageContainer.innerHTML = messageContainer.innerHTML + message + '<BR>';
messageContainer.scrollTop = messageContainer.scrollHeight;
}
};
const toggleMsgPnl = () => {
let msgPnl = document.getElementById("message").classList;
msgToggle = msgToggle ? 0 : 1;
if ( msgToggle ) { msgPnl.remove( 'd-none' )} else { msgPnl.add( 'd-none' )}
}
/* ------------------------------------------------------------------------------------------ */
/*** -- ONE TIME ACTION for Parent App, possibly handled by deployment scripts ***/
// In this example page, for the sake of demonstration, we are first checking to see
// if it already exists and cleaning up by deleting it before recreating it and
// and retrieving the new secret. This is normally a one time action on instance deployment
// as it is just used as a handshake mechannism between the parent app and the Logi Server
const getClients = async () => {
let clientExists = await Get( `${ComposerUrl}/api/trusted-access/clients`, SupervisorCreds, "Basic" )
.then(( result ) => {
Client = result.content.find( x => x.client_name === ClientName );
return Client
});
return clientExists;
}
const deleteClient = async ( client_id ) => {
deletedClient = await Del( `${ComposerUrl}/api/trusted-access/clients/${client_id}`,SupervisorCreds, 'Basic' );
return deletedClient;
}
async function processClient() {
async function createClient(){
// create/re-create client
var ReqBody = {
"access_token_validity_seconds": 2000,
"client_name": ClientName
};
result = await Post( `${ComposerUrl}/api/trusted-access/clients`, SupervisorCreds, "Basic", ReqBody )
.then(( result ) => {
ClientCredsForAccessTokenGeneration = window.btoa( `${ result.client_id }:${ result.client_secret }` );
localStorage.setItem( 'ComposerTAClientKey', ClientCredsForAccessTokenGeneration );
logMessage( "client response: <pre>" + JSON.stringify( result, undefined, 2 )) + "</pre><BR>";
return ClientCredsForAccessTokenGeneration
});
return result;
};
async function validateClient() {
// first check localStorage for stored client secret info
try {
var localTAClient = localStorage.getItem( 'ComposerTAClientKey' );
} catch( e ) {
console.log( "localTAClient is not available in localStorage" );
}
// check whether client is registered in Composer - not required in normal deployments
let clientExists = await getClients();
// if the client exists and there is a saved secret
if ( clientExists && !localTAClient ) {
deletedClient = await deleteClient( clientExists.client_id );
} else if ( clientExists && localTAClient ) {
logMessage( "client response: <pre>" + atob( localTAClient ) + " client secret exists in localStorage </pre><BR>" );
return localTAClient;
}
try {
createClient();
} catch ( e ) {
console.log( e );
deletedClient = await deleteClient( clientExists.client_id );
console.log( deletedClient );
}
};
ClientCredsForAccessTokenGeneration = await validateClient();
}
/*** -- PARENT APP BACKEND FUNCTIONS coded by developers in Java, .NET, nodeJS/ExpressJS, Python, PHP, ... ***/
// In this example page, and for the sake of easily demonstrating the TA workflow, we are
// performing these tasks in the HTML on the client side. This should never be the case in a
// production deployment where creating access tokens or provisioning users
function createAdminAccessToken() {
var ReqBody = {
"username": AdminUsername
};
Post( `${ComposerUrl}/api/trusted-access/token`, ClientCredsForAccessTokenGeneration, "Basic", ReqBody )
.then(( result ) => {
AdminAccessToken = result;
logMessage("admin access token: <pre>" + JSON.stringify(result, undefined, 2)) + "</pre><BR>";
});
}
function getGroups() {
Get( `${ComposerUrl}/api/groups`, AdminAccessToken.access_token )
.then(( result ) => {
AdminGroups = result.content;
logMessage( "groups available to admin: <pre>" + JSON.stringify( result, undefined, 2 ) ) + "</pre><BR>";
})
}
// NOTE: When provisioning the end user, this may be driven by the parent app, or we provision "group users" if
// limiting the user management tasks in Logi Composer. In this sample we are hard-coding the Administrators
// group. This can be changed to another group with different security settings if desired, or set dynamically
function provisionEndUser() {
// check if user exists
var userExists = Get( `${ComposerUrl}/api/users`, AdminAccessToken.access_token )
.then(( result ) => {
if ( result.content.find( ( g ) => g.name === EndUsername ) ) {
// could delete user instead?
logMessage( "user exists: <pre>" + JSON.stringify( EndUsername, undefined, 2 )) + "</pre><BR>";
return true;
} else {
var ReqBody = {
"name": EndUsername,
"accounts": [
{
"accountId": AdminGroups[0].accountId,
"groups": [
AdminGroups.find(g => g.label === EndUserGroup ).id
],
"roles": [],
"userAttributes": [
{ "key": "org_id",
"value": "USA",
"encrypted": false
},{
"key": "Disaster",
"value": "Severe Storm,Tropical Cyclone,Flooding,Wildfire",
"encrypted": false
}]
}
],
"fullname": EndUsername
}
Post( `${ComposerUrl}/api/users`, AdminAccessToken.access_token, "Bearer", ReqBody )
.then(( result ) => {
EndUser = result;
logMessage("user created: <pre>" + JSON.stringify( result, undefined, 2 )) + "</pre><BR>";
});
}
});
}
function createEndUserAccessToken() {
var ReqBody = {
"username": EndUsername
};
Post(`${ComposerUrl}/api/trusted-access/token`, ClientCredsForAccessTokenGeneration, "Basic", ReqBody)
.then((result) => {
EndUserAccessToken = result;
logMessage("end user access token: <pre>" + JSON.stringify(result, undefined, 2)) + "</pre><BR>";
});
}
/*** PARENT APP FRONTEND FUNCTIONS FOR EMBEDDING and listing Composer Content. Get token from Backend, using logic defined in parent backend that communicates to Composer ***/
// once the end user is provisioned and the end user access token (bearer token) is generated,
// the token is passed to the client browser from the parent app server
function getDashboardsAvailableToEndUser() {
Get(`${ComposerUrl}/api/inventory`, EndUserAccessToken.access_token)
.then((result) => {
EndUserDashboards = result.content;
//Add create new dashboard button
document.getElementById('dashboards').innerHTML = document.getElementById('dashboards').innerHTML + `<button id="newdashboard" onclick="addNewDashboard()" class="btn btn-secondary btn-sm me-2 mb-1"> Create new dashboard</button>`;
logMessage("dashboards available to end user: <pre>" + JSON.stringify(result, undefined, 2)) + "</pre><BR>";
for ( const dashboard of EndUserDashboards ) {
document.getElementById('dashboards').innerHTML = document.getElementById('dashboards').innerHTML + `<button id="dashboard-${dashboard.inventoryItemId}" onclick="EmbedDashboard('${dashboard.inventoryItemId}')" class="btn btn-info btn-sm me-1 mb-1">${dashboard.name}</button>`;
}
})
}
function EmbedDashboard( dashboardid ) {
const embedManagerPromise = window.initComposerEmbedManager({
initialToken: EndUserAccessToken.access_token // tokenValue is provided by the Trusted Access API
});
embedManagerPromise.then((embedManager) => {
//2021-09-03 GM: Updated to clean-up old dashboard components before adding a new one
for ( var component in embedManager.componentList ) {
embedManager.removeComponent( component);
}
componentConfig.dashboardId = dashboardid;
embedManager.createComponent( 'dashboard', componentConfig )
.then(dashboard => dashboard.render(
document.getElementById( "embedDash" ), // htmlElement
{ width: '100%', height: '100%' }
))
});
}
function addNewDashboard() {
const embedManagerPromise = window.initComposerEmbedManager({
initialToken: EndUserAccessToken.access_token // tokenValue is provided by the Trusted Access API
});
//delete dashboardId from componentConfig if exists
delete componentConfig.dashboardId;
embedManagerPromise.then(( embedManager ) => {
//2021-09-03 GM: Updated to clean-up old dashboard components before adding a new one
for ( var component in embedManager.componentList ) {
embedManager.removeComponent( component);
}
embedManager.createComponent( "dashboard", componentConfig ).then( component => {
component.render(document.getElementById( 'embedDash' ), { width:"100%", height: "100%" });
})
});
}
</script>
</head>
<body>
<div class="container">
<h2>Composer 6.9 Trusted Access and Embed Manager Workflow</h2>
<div id="controls" class="mb-3">
<i class="d-block text-muted">*** Performed once during instance deployment - manual or scripted ***</i>
<button id="createClient" onclick="processClient()" class="btn btn-success btn-sm">1. Create Client</button>
</div>
<div id="controls" class="mb-3">
<i class="d-block text-muted">*** Parent application server-side functions ***</i>
<button id="createAdminAccessToken" onclick="createAdminAccessToken()" class="btn btn-warning btn-sm">2. Generate
Admin Token</button>
<button id="getGroups" onclick="getGroups()" class="btn btn-warning btn-sm">3. Get Groups</button>
<button id="provisionEndUser" onclick="provisionEndUser()" class="btn btn-warning btn-sm">4. Create End
User</button>
<button id="createEndUserAccessToken" onclick="createEndUserAccessToken()" class="btn btn-warning btn-sm">5.
Generate
End User Token</button>
</div>
<div id="controls" class="mb-3">
<i class="d-block text-muted">*** Parent application client-side functions ***</i>
<button id="getDashboardsAvailableToEndUser" onclick="getDashboardsAvailableToEndUser()"
class="btn btn-info btn-sm">6. Get Dashboards available to End User</button>
<button id="toggleMsgPnl" onclick="toggleMsgPnl()"
class="btn btn-sm text-muted float-end">+/- Messages</button>
</div>
<div id="message" class="mb-3"></div>
<div id="dashboards" class="mb-3"></div>
<div id="embedDash" class="mb-3" style="position:relative; height: 80vh; border:dashed 1px red;"></div>
</div>
</body>
</html>
Please sign in to leave a comment.
Comments
0 comments