Skip to main content
pdf?stylesheet=default
Blackboard Help

Access ID

The Access ID authentication plugin is used to single sign on (SSO) a user to Joule from an external system such as Campus Vue. The Access ID plugin works by using a shared secret between Joule and the external system. Based on this shared secret and a configured administrative login, the external system generates a one time use token that can be used to craft a unique URL. The external system provides the unique URL to the user and once clicked, Joule validates the user's access based on the IP of the external system as well as the unique token and shared secret. This system is a secure token based authentication using a shared system for constructing and deconstructing generated tokens.

The Access ID authentication plugin must be installed and enabled before you can update the settings.


Install & enable Access ID authentication

Blackboard installs and enables Access ID authentication for you. Talk to your account executive or engagement manager about adding it to your Moodlerooms instance, if you don't already have it.


Access ID global settings

Steps: Administration > Site administration > Plugins > Authentication > Access ID authentication

  1. From Site administration select Plugins, Authentication, and Access ID authentication.
  2. Configure the general settings.

    • Allowed IP list: Enter all IPs that can communicate with the authentication plug-in. Put every entry on one line. Only full IP addresses or addresses in CDIR notation will be accepted as valid entries.
    • Secret key: The secret key entered must be identical to the one configured on the side of the system with which you are configuring the single sign-on from.
    • Username: Specify the username for accessing the endpoint URL.
    • Password: Specify the password for accessing the endpoint URL.
    • User Lookup: Specify Joule user profile field used for user lookups.
      • idnumber
      • username
    • Access ID Duration: Specify the duration that a generated Access ID is valid (in minutes). Default: 5 mins
    • Hashing algorithm: Specify hash algorithm validation for incoming parameters.
      • SHA-256 (default)
      • SHA-1

Lock User Fields

The lock user fields section of the authentication plugin's settings is used to lock a user's access to update a user profile field's value. This is useful for user data that is updated via an external system or that the administrator doesn't want the user to change after it has been entered. If you are locking fields that are required by Moodle, make sure that you provide that data when creating user accounts or the accounts will be unusable. Consider setting the lock mode to 'Unlocked if empty' to avoid this problem. Administrators can only lock the following profile field:

  • First name
  • Surname
  • Email address
  • City/town
  • Country
  • Language
  • Description
  • Web page
  • ID number
  • Institution
  • Department
  • Phone 1
  • Phone 2
  • Address

Each profile field can be set to one of the following settings with the default being unlocked:

  • Unlocked: The user can change the value in the profile field at anytime.
  • Unlocked if empty: The user can only set the value for the user profile field once. If the value is set by an external system the user cannot change it in Joule.
  • Locked: The user cannot change the user profile field's value in Joule, it must be changed by an administrator or by an external system.

Access ID Workflow Example

Single Sign-On Workflow Example

The following is a workflow example for the setup of Access ID Authentication Single Sign-On.

Step 1: Generate Token

The first step in the Access ID Authentication Single Sign-On requires a token to be generated using a pre-shared secret. See Access ID Authentication above for sample code on generating the token.

Step 2: Validate Token

The Client Server sends a request to the web service endpoint for token generation, located at https://site.url/auth/accessid/webservices.php. Replace "site.url" with your Joule site's domain name. The request must be a POST over secure channels (HTTPS). Additionally, the request from the Client Server must validate within the range set by the Allowed IP list Access ID Authentication Settings. The returned "accessid" is valid for the duration specified by the Access ID Duration.

The following information should be passed with each post for token generation:

  • username: The pre-established username. This is configured in the Access ID's Authentication Settings within Joule.
  • pass: The pre-established password. This is configured in the Access ID's Authentication Settings within Joule.
  • timestamp: The same timestamp that was used to generate the token.
  • token: The token, generated in step 1.
  • userid: The userid, as defined by the User Lookup field in the Access ID's Authentication Settings within Joule. For example, if the User Lookup setting is set to "idnumber", then the authenticating user's idnumber would be passed in this field.

Example Success Response

​<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<auth_accessid_lib_server_service generator="zend" version="1.0">
  <get_accessid>
    <response>
      <accessid>hojHWTuH8oClZjfR</accessid>
    </response>
    <status>success</status>
  </get_accessid>
</auth_accessid_lib_server_service>

Example Error Response

<?xml version="1.0" encoding="UTF-8"?>
<rest generator="zend" version="1.0">
    <response>
        <message>Remote IP address of 111.111.111.111 failed to validate. If remote IP address is correct, then validate the Allowed IP list setting.</message>
    </response>
    <status>failed</status>
</rest>

Step 3: Redirect User to Joule

Once the accessid has been obtained, the user should be redirected to the endpoint in Joule to automatically log the user into Joule: https://site.url/auth/accessid/access...?id=[ACCESSID]

Optionally, the user may be redirected to a page within Joule. This is common when the user should be redirected to a specific course, instead of being redirected to the Front Page or My Moodle. In this case, a URL encoded "redirect" parameter would be passed: https://site.url/auth/accessid/access...ct=[LOCAL_URL]

For course redirection, the parameters could be:

  • %2Fcourse%2Fview.php%3Fid%3DXXX
  • %2Fcourse%2Fview.php%3Fname%3DXXX
  • %2Fcourse%2Fview.php%3Fidnumber%3DXXX

Where XXX is the URL encoded value of either the course's internal ID, shortname, or idnumber respectively. The user can be redirected anywhere within Joule.

Example Diagram

Access ID Code Examples

The following code examples compare a generated hash using the variables:

  • Shared Secret: GerwtYxxd34
  • userid: janedoe
  • timestamp: 1326827023
  • username: jdoe
  • pass: pass

Expected token: 153283f1909be96a23a3324b345098010320b0db1fd71a726bbad0ca3cfd67ff

Java Example 1

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* @author Darko Miletic
*
*/
public class Sample {

    public static String generate_token_impl(String[] params, String secret, String algorithm) {
        MessageDigest algo = null;
        try {
            algo = MessageDigest.getInstance(algorithm);
            StringBuilder sb = new StringBuilder();
            for (String param : params) {
                String salted_param = secret + param;
                sb.append(salted_param);
            }
            return byte2hex(algo.digest(sb.toString().getBytes()));
        } catch (NoSuchAlgorithmException excp) {
            return "";
        }
    }

    public static String generate_token(String[] params, String secret) {
        return generate_token_impl(params, secret, "SHA-256");
    }

    public static String byte2hex(byte[] hash) {
        StringBuilder sb = new StringBuilder();
        for (byte b : hash) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        String[] testparams = {"janedoe", "1326827023", "jdoe", "pass"};
        String secret = "GerwtYxxd34";
        String known_result = "153283f1909be96a23a3324b345098010320b0db1fd71a726bbad0ca3cfd67ff";
        String result = generate_token(testparams, secret);
        if (result.equals(known_result)) {
            System.out.println("Hashes match");
        } else {
            System.out.println("Hashes DO NOT match:");
            System.out.println(result);
        }
    }

}

Java Example 2

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HttpsURLConnection;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;


public class AccessIDGenerator {

private final static String USER_AGENT = "Mozilla/5.0";
private final static String FORWARD_URL =  "https://cccccccc-sandbox.mrooms.net/auth/accessid/access.php?id=";
public static String generate_token_impl(String[] params, String secret, String algorithm) {
MessageDigest algo = null;
try {
algo = MessageDigest.getInstance(algorithm);
StringBuilder sb = new StringBuilder();
for (String param : params) {
String salted_param = secret + param;
sb.append(salted_param);
}
return byte2hex(algo.digest(sb.toString().getBytes()));
} catch (NoSuchAlgorithmException excp) {
return "";
}
}

public static String generate_token(String[] params, String secret) {
return generate_token_impl(params, secret, "SHA-256");
}

public static String byte2hex(byte[] hash) {
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}

public static String getAccessID(String token) {

try {
String url = "https://cccccccc-sandbox.mrooms.net/auth/accessid/webservices.php"; URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

//add request header
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");

String urlParameters = "username=" + username + "&pass=" + pass + "&timestamp=" + timestamp + "&token=" + token + "&userid=" + userid;

// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();

int responseCode = con.getResponseCode();
System.out.println("\nSending 'POST' request to URL : " + url);
System.out.println("Post parameters : " + urlParameters);
System.out.println("Response Code : " + responseCode);

BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

//print result
//System.out.println(response.toString());
return response.toString();
} catch (Exception l_ex) {

}
return "";

}

static String parseAccessId (String xmlString) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

// Load the input XML document, parse it and return an instance of the
// Document class.
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmlString));

Document document = builder.parse(is);

NodeList nodeList = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);

if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
System.out.println(element.getNodeName());
NodeList name = element.getElementsByTagName("accessid");
Element line = (Element) name.item(0);
return getCharacterDataFromElement(line);

}
}
} catch (Exception l_ex) {
l_ex.printStackTrace();
}
return "";
}

public static String getCharacterDataFromElement(Element e) {
Node child = e.getFirstChild();
if (child instanceof CharacterData) {
CharacterData cd = (CharacterData) child;
return cd.getData();
}
return "";
}
/**
* @param args
*/
public static void main(String[] args) {
timestamp = Long.toString((long)System.currentTimeMillis()/1000);
System.out.println("Timestamp; " + timestamp);
String[] testparams = {userid, timestamp,username, pass};
String secret = "GerwtYxxd34";
String known_result = "153283f1909be96a23a3324b345098010320b0db1fd71a726bbad0ca3cfd67ff";
String result = generate_token(testparams, secret);
System.out.println("Token is: " + result);
/*
if (result.equals(known_result)) {
System.out.println("Hashes match");
} else {
System.out.println("Hashes DO NOT match:");
System.out.println(result);
}
*/

String response = getAccessID(result);
System.out.println("Parsing the XML String: "+ response);

String accessId = parseAccessId(response);
System.out.println("Access ID: " + accessId);
System.out.println ("URL: " + FORWARD_URL + accessId);

}

private static String username = "joe.NAME";
private static String pass = "gaaaps22GPS@";
private static String userid = "joe.instructor";
private static String timestamp = null;
}

PHP Example

/**
* @param array $params
* @param string $secret
* @return string
*/
function generate_token(array $params, $secret) {
    $hashstorage = '';
    foreach ($params as $param) {
        $hashstorage .=  $secret.$param;
    }
    $result = hash('SHA256', $hashstorage);
    return $result;
}

$secret = 'GerwtYxxd34';
$testparams = array(
    'userid'    => 'janedoe',
    'timestamp' => '1326827023',
    'username'  => 'jdoe',
    'pass'      => 'pass'
);
$known_result = '153283f1909be96a23a3324b345098010320b0db1fd71a726bbad0ca3cfd67ff';
$result = generate_token($testparams, $secret);
if ($result === $known_result) {
    echo 'Hashes match!'.PHP_EOL;
} else {
    echo 'Hashes DO NOT match!'.PHP_EOL;
}