How to get a system user ticket credential
To get the ticket, you must send a request containing a signed version of your system user token to the partner system user service
endpoint.
The system user token is on of the claims in the id_token
received the initial administrative authentication.
Pre-requisites
- Client secret (obtained when application is registered)
- ContextIdentifier (tenant identity)
- System user token (application is authenticated)
There are several ways to obtain a system user ticket:
- Use our REST API, or
- Use our SOAP API, or
- Use one of our nuget packages
All three options require a signed version of the system user token. Please read the How to sign a system user token documentation to learn how to do that part of this flow.
Use the REST API
SuperOffice CRM Online exposes one REST endpoint for conducting the exchange:
https://{environment}.superoffice.com/Login/api/PartnerSystemUser/Authenticate
The following example is demonstrates the HTTP request.
@signed_Token=YOUR_SIGNED_TOKEN
@client_Secret=YOUR_CLIENT_SECRET
@context_Identifier=YOUR_CUSTOMER_ID
POST https://{environment}.superoffice.com/Login/api/PartnerSystemUser/Authenticate
Content-Type: application/json
Accept: application/json
{
"SignedSystemToken": "{{signed_Token}}",
"ApplicationToken": "{{client_secret}}",
"ContextIdentifier": "{{context_Identifier}}",
"ReturnTokenType": "JWT"
}
Use the SOAP API (NodeJS)
SuperOffice CRM Online exposes one WCF SOAP endpoint for conducting the exchange:
https://onlineenv.superoffice.com/login/services/PartnerSystemUserService.svc
Download the PartnerSystemUserService WSDL
The SOAP envelope should contain 2 header elements and the AuthenticationRequest
element in the SOAP body.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://www.superoffice.com/superid/partnersystemuser/0.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://www.superoffice.com/superid/partnersystemuser/0.1"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<tns:ApplicationToken>${appToken}</tns:ApplicationToken>
<tns:ContextIdentifier>${contextId}</tns:ContextIdentifier>
</SOAP-ENV:Header>
<ns0:Body>
<ns1:AuthenticationRequest>
<ns1:SignedSystemToken>${signedToken}</ns1:SignedSystemToken>
<ns1:ReturnTokenType>Jwt</ns1:ReturnTokenType>
</ns1:AuthenticationRequest>
</ns0:Body>
</SOAP-ENV:Envelope>
Use a nuget package
We provide the following .NET nuget packages to help perform the task.
SuperOffice.WebApi (preferred)
SuperOffice.Crm.Online.Core (legacy)
How to get system user ticket
The following example code has an extensive amount of logging to the console. This lets you see the output from each step. The final output is the system user ticket. It performs the following steps:
- Read the partner's private PEM file.
- Sign system user token.
- Send request to SuperOffice.
- Gets the response to obtain the JWT.
- Validate JWT token.
- Extract the system user ticket.
NPM package.json file for NodeJS REST example.
package.json file
{
"name": "devnet-nodejs-systemuser-rest",
"version": "1.0.0",
"description": "Signs system user token and requests system user ticket",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "AnthonyYates",
"license": "MIT",
"dependencies": {
"axios": "^0.26.1",
"crypto": "^1.0.1",
"jsonwebtoken": ">=9.0.0",
"moment": "^2.22.2"
}
}
index.js code
const crypto = require('crypto');
const moment = require('moment');
const fs = require('fs');
const axios = require('axios');
const jwt = require('jsonwebtoken');
// specify the environment
// sod => development
// qastage => stage
// online => production
const env = 'sod';
// Partner Application Token (AKA Client Secret)
const appToken = 'YOUR_APPLICATION_TOKEN_GOES_HERE';
// Your Online Sandbox Customer Identifier
const contextId = 'Cust12345';
// SystemUserToken provided as a claim in the callback (Redirect URL)
// when a tenant administrator successfully signs into SuperID.
const systemToken = 'YOUR_SYSTEM_USER_TOKEN_GOES_HERE';
// Partners private key
const privKeyFile = 'privatekey.pem';
// SuperOffice public key (SOD)
// Open SuperOfficeFederatedLogin.crt in notepad,
// save contents as SuperOfficeFederatedLogin.pem
const publKeyFile = 'SuperOfficeFederatedLogin.pem';
const getSystemUserTicket = async () => {
try {
const privateKeyFile = fs.readFileSync(privKeyFile,'utf8');
const publicKeyFile = fs.readFileSync(publKeyFile, 'utf8');
// prepare the datetime stamp
const utcTimestamp = moment.utc().format('YYYYMMDDHHmm');
const data = `${systemToken}.${utcTimestamp}`;
log('Token.Time: ' + data);
// sign the System User token
let sign = crypto.createSign('SHA256');
sign.update(data);
sign.end();
sign = sign.sign(privateKeyFile, 'base64');
const signedToken = `${data}.${sign}`;
log('Signed Token: ' + signedToken);
// send the request
var postData = {
'SignedSystemToken': `${signedToken}`,
'ApplicationToken': `${appToken}`,
'ContextIdentifier': `${contextId}`,
'ReturnTokenType': 'JWT'
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
"Accept": "application/json;charset=UTF-8"
}
};
const jwtRes = await axios.post(`https://${env}.superoffice.com/Login/api/PartnerSystemUser/Authenticate`, postData, axiosConfig);
if(jwtRes.data.IsSuccessful)
{
var token = jwtRes.data.Token;
var verifyOptions = {
ignoreExpiration: true,
algorithm: ["RS256"]
};
// validate the JWT and extract the claims
var decoded = jwt.verify(token, publicKeyFile, verifyOptions);
// write out the ticket to the console, DONE!
const ticket = decoded["http://schemes.superoffice.net/identity/ticket"];
return ticket;
} else {
log('Getting the System User ticket was unsuccessful: ' + jwtRes.data.ErrorMessage);
}
} catch (error) {
log("Error: " + error);
}
}
function log(message) {
console.log('')
console.log(message)
}
// Finally, execute the function to get the system user ticket!
(async () => {
try {
const result = await getSystemUserTicket();
log("System User Ticket: " + result);
} catch (error) {
log(error);
}
})();