Guru's Verification engine ensures consistency, confidence, and trust in the knowledge your organization shares. Learn more.

Custom Image Service Specification

Fusion provides the ability to deliver student and/or staff photos to be consumed within various areas in the Fusion Platform via a custom image service.

Fusion looks up an individual member's photo using the Image Service ID(string) that is assigned to that member. This is configured in General Settings > External ID Types area of Fusion System Preferences.

image.png

In the settings above, the image service ID is set to use Student ID. The image service ID can be configured to use whatever identifier is most convenient for your custom image service.

For a custom image service to be compatible for use in Fusion it needs to be a RESTful web service with an API endpoint that can accept an id number and optionally a hash. Fusion also supports the use of OAuth for authentication.

Your endpoint will ultimately be configured in the General Settings > System Settings area of Fusion System Preferences.

image.png

Web Service URL: This is your image service endpoint; what it looks like will depend on your implementation. From the Fusion side we provide two placeholders.

  • {IdNumber} This relates to the external ID that is configured as the Image Service ID. This will allow you to identify the member that we are fetching an image for.
  • {Hash} This will add a hash into the endpoint that's computed using the member ID number and a shared secret key that's accessible from Fusion and your custom image service.

Verifying Using a Hash

One way to verify that requests to your custom image service are coming from Fusion is to use a hash matching. The method we recommend for creating hash values is SHA 512.

From the Fusion side, the shared key that's used to compute the hash value is stored in your instance of the Fusion database. Its location is in T_SYSTEM_SETTINGS table, in the field IMAGE_SERVICE_SHARED_KEY.

The {Hash} that can be replaced in the web service URL is computed using the Encoding/Hash Method that is configured in Fusion. The hash is built by concatenating the {IdNumber} and the shared key, utf-8 encoding the string, and then hashing the string with the configured method. From your custom image service you can use this value to do hash matching. See Example 2 below.

Options for Encoding/Hash Method:

  • None (No hashing, just Id Number as a parameter in the web service URL)
  • MD5 (ID +Key)
  • SHA 1 (ID + Key)
  • SHA 256 (ID + Key)
  • SHA 512 (ID + Key) **Recommended

Authentication Type

None

  • If you choose to use hash matching for verification, authentication type can be set to none.

Basic Authentication

  • Basic Auth. Username and Password for authentication
  • A base64 string converted from a bytearray of username:password encoded in iso-8859-1 is added into the "Basic" header.
  • Considered an out of date security protocol. Included for legacy clients but is generally not recommended for use.
private static void addBasicAuthentication(HttpClient client, string username, string password){    var byteArray = Encoding.GetEncoding("iso-8859-1").GetBytes(string.Format("{0}:{1}", username, password));    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));}

Configuring OAuth

  • As of v3.7, we support the use of OAuth 2.0 for authentication with 2 options
    • Password(does not use Client Id)
    • Client Credentials
    • However, there are two major issues
      • Token call made client side potentially exposing client creds on request(security risk)
      • OAuth token gets called for every image request so if the token endpoint refreshes automatically, it breaks bulk image uploads by invalidating previously generated tokens before they can make the image call.
  • As of v3.8.5, we have resolved the two major issue in 3.7 with OAuth
    • Token call moved to server side to prevent credential exposure risk.
    • Token caching added to prevent token call from being made again until the previous token has expired. Enables re-use of 1 token for bulk image searches.

Example Config(with image service code in Node.js)

The following code samples are based off of our example image service built on Node.js which can be found at the following git repo . Note that this example makes use of third-party libraries which may need to be installed via npm or other package manager.

git clone https://github.com/InnoSoftFusion/FusionImageService.git


Image Fetching Logic

Consistent across all the following examples is the getImage() function. This simple function takes in a response object res and an external ID. It checks our image folder(in this case, our base app location) for an image file bearing that name. If it exists, we return the image and if not, we return a default image. (i.e. missing user image). This is one way to do image lookup, you may choose to do it differently.

 let fs = require('fs');   function getImage(res, externalId){        var fileName = externalId + '.jpg';    var stream    if (fs.existsSync(fileName)) {        stream = fs.createReadStream(fileName);            } else {        stream = fs.createReadStream("default.jpg");            }    stream.on('open', function () {        res.set('Content-Type', 'image/jpeg');        stream.pipe(res);    }); }

Note in Line 13, we set the Content-Type to 'image/jpeg' on our response object.

On Line 14, the function uses stream.pipe() to write the image file steam to our response object res. Read more here: https://nodejs.org/en/knowledge/advanced/streams/how-to-use-stream-pipe/

Example 1 : No Auth

Here we simply pass the external Id to the API endpoint, call our getImage() function as defined above and return the image as the response. Please note this setup is not recommended for production and is for demonstration purposes only. We recommend you use either hashing, authentication or both to validate requests.

image.png

app.get('/api/person/getPersonImageNoAuth/:externalId', async function (req, res, next) {     getImage(res, req.params.externalId); }); 

EXAMPLE 2: HASH VERIFICATION

Building on example 1, here we additionally pass in a hash value and call a verifyHash() function before calling our getImage() function. If verfiyHash() evaluates as false, we return a response indicating the hash was invalid.

The {Hash} that gets replaced in the web service URL is computed from a string composed by concatenating {IdNumber} and IMAGE_SERVICE_SHARED_KEY which is then utf-8 encoded. In Line 19 of the sample below we use a node.js package to create a utf-8 encoded string consisting of the external id that's passed from Fusion and the shared image service key. We then create a similar hash value using the same algorithm that's set in Fusion and compare it to the hash value that's passed in to make sure they match.

image.png

 var hashingAlgorithm = 'sha512';//md5 | sha1 | sha256 | sha512 | Must match Fusion setting var sharedKey = '123456abcdef';//Must also be configured in Fusion var crypto = require('crypto'); const utf8 = require('utf8');//requires npm install utf-8  app.get('/api/person/getPersonImage/:externalId/:hash', async function (req, res, next) {      if(verifyHash(req.params.hash,req.params.externalId))        {              getImage(res, req.params.externalId);        }    else        {               return res.status(400).send('Invalid Hash')        } });   function verifyHash(incomingHashValue, externalId){    var hash = crypto.createHash(hashingAlgorithm);    var toHash = utf8.encode(externalId + sharedKey);     hash.update(toHash);    var hashValue = hash.digest('hex');    return hashValue === incomingHashValue; } 

EXAMPLE 3: OAUTH

The OAuth example provided here uses the client credentials grant type which uses client id and client secret to get an OAuth token. The node package provided in this example service is not compatible with Fusion's implementation of the Password grant type which sends only Username and Password to the token endpoint. Please see the application README.md file for more info.

image.png

var clientCreds = [{ clientId : '{clientId}', clientSecret : '{clientSecret}', redirectUris : [''], grants: ['client_credentials'] }];var userCreds = [{ id : '123', username: '{username}', password: '{password}', grants: ['password'] }];const memoryStore = new MemoryStore(clientCreds, userCreds)app.oauth = new OAuthServer({        model: memoryStore,    requireClientAuthentication: {password: false}  });app.oauth.token.requireClientAuthentication = {password: false}// Authorization Endpointapp.post('/token', app.oauth.token());// OAuth Endpointapp.get('/api/person/getPersonImage/:externalId', app.oauth.authenticate(), async function (req, res, next) {     getImage(res, req.params.externalId); });

You must have Author or Collection Owner permission to create Guru Cards. Contact your team's Guru admins to use this template.