Note: This article is valid for Cloud solutions only.
Non-interactive login without using the user's refresh token and searching for users and group
The OpenID support has been expanded to incorporate non-interactive login without using the user's refresh token and searching for users and groups. To do this, the original login script has been renamed to script and has been expanded to allow for more than just one function.
You are allowed to make as many functions as needed in the script, but the following functions are reserved for specific purposes:
Function signature | Description |
interactive_login(token, access_token) |
This is the old function to handle interactive users (e.g. a user that wants to log on to TARGIT or Anywhere). It will be called after the TARGIT Server has obtained an ID token, access token for the user logging in
The function should return an object like this: {
{ The name and display_name properties are only for display purposes in the editor. It is only the group id that is being used to evaluate group membership in roles/rights. You can freely mix the short-form and object form: [ "id1", { id: "id2", name: "group2", display_name: "group2 display name" }, "id3" ]
|
noninteractive_login(username) |
This function is used when a user is logged on non-interactively - i.e. when running a scheduled job for a user.
The function should return an object in the same format as interactive_login(). It should retrieve the user's information using e.g. the Microsoft Graph API (if using Azure AD) or something similar |
user_search(search_string) |
This function is used when editing roles/rights to search for users.
The function should return an array of items with the same format as the items in user_groups from interactive_login/noninteractive_login. The id will be prefixed with the identity provider ID + backslash if PrefixUserIDs is enabled on the identity provider. |
group_search(search_string) |
This function is used when editing roles/rights to search for groups.
The function should return an array of items with the same format as the items in user_groups from interactive_login/noninteractive_login. |
The following global variables are defined when any of the functions are invoked:
Name | Description |
CLIENT_ID | The client id for the identity provider |
CLIENT_SECRET | The client secret |
TOKEN_ENDPOINT | The token endpoint for the identity provider |
LoginApp | What application is trying to log on (Windows, Anywhere, MS) - only applicable for interactive_login/noninteractive_login |
CLOUD_TENANTID | Cloud only: The tenant ID |
CLOUD_STAGEID | Cloud only: The stage ID |
The following objects are declared:
Name | Description |
lookup |
Used to query users/groups using the selected security model. It will e.g. query the Active Directory if the security model is set to Windows. Methods:
|
HTTP |
Used to issue REST queries Methods:
|
HTTPFetchOptions |
Options object for HTTP.fetch() Properties:
|
Cache |
Simple cache for the scripts. Remember that the cache is global for all users and identity providers, so use some clever cache key schemes to avoid "pollution" from other users/providers Methods:
|
console |
Class to generate debugging output. Methods:
|
JSON |
Class to encode/decode JSON Methods:
|
This example script is suitable on an Azure AD tenant where the local AD is synchronized with Azure AD and you want to use the local security identifiers and account names. If you have a Azure AD tenant without any synchronization you would want to use some of the other properties of users and groups in the Graph API (like id and userPrincipalName for instance).
Example script for Azure AD using Microsoft Graph API to get user information:
var domain_prefix = "DomainName\\";
var upn_suffix = "@YourDomain.com";
async function getGroups(accessToken, userID) {
var fetchOptions = { headers: { Authorization: "Bearer " + accessToken } };
var groups = [];
var nextUrl = HTTP.url_encode("https://graph.microsoft.com/v1.0/users/{user}/memberOf", { user: userID }, { "$select": "displayName,onPremisesSamAccountName,onPremisesSecurityIdentifier" });
do
{
var data = JSON.parse(await HTTP.fetch(nextUrl, fetchOptions));
groups.push(...data.value.map(g => ({ id: g.onPremisesSecurityIdentifier, name: domain_prefix + g.onPremisesSamAccountName, display_name: g.displayName })));
nextUrl = data["@odata.nextLink"];
} while (nextUrl)
return groups;
}
async function interactive_login(token) {
var usernameparts = token["preferred_username"].split("@");
var accessToken = await HTTP.login_client_credentials(TOKEN_ENDPOINT, CLIENT_ID, CLIENT_SECRET, "https://graph.microsoft.com/.default");
return {
user_name: domain_prefix + usernameparts[0],
user_id: token.oid,
email: token.email,
user_groups: await getGroups(accessToken, token.oid)
};
}
async function noninteractive_login(username) {
var parts = username.split("\\");
var upn = parts[parts.length-1] + upn_suffix;
var accessToken = await HTTP.login_client_credentials(TOKEN_ENDPOINT, CLIENT_ID, CLIENT_SECRET, "https://graph.microsoft.com/.default");
var userUrl = HTTP.url_encode("https://graph.microsoft.com/v1.0/users/{user}", { user: upn }, { "$select": "id,onPremisesSamAccountName,mail" });
var fetchOptions = { headers: { Authorization: "Bearer " + accessToken } };
var userData = JSON.parse(await HTTP.fetch(userUrl, fetchOptions));
return {
user_name: domain_prefix + userData.onPremisesSamAccountName,
user_id: userData.id,
email: userData.mail,
user_groups: await getGroups(accessToken, userData.id)
};
}
function quote(s) {
return '"' + String(s).replace('"', '\\"') + '"';
}
async function user_search(searchString) {
var accessToken = await HTTP.login_client_credentials(TOKEN_ENDPOINT, CLIENT_ID, CLIENT_SECRET, "https://graph.microsoft.com/.default");
var params = { "$select": "id,onPremisesSamAccountName,displayName" }
if (searchString) {
params["$search"] = quote('displayName:' + searchString);
}
var nextUrl = HTTP.url_encode("https://graph.microsoft.com/v1.0/users", { }, params);
var fetchOptions = { headers: { Authorization: "Bearer " + accessToken, ConsistencyLevel: "eventual" } };
var result = [];
do {
var users = JSON.parse(await HTTP.fetch(nextUrl, fetchOptions));
result.push(...users.value.filter(u => u.onPremisesSamAccountName).map(u => ({ id: u.id, name: domain_prefix + u.onPremisesSamAccountName, display_name: u.displayName })));
nextUrl = users["@odata.nextLink"];
} while (nextUrl);
return result;
}
async function group_search(searchString) {
var accessToken = await HTTP.login_client_credentials(TOKEN_ENDPOINT, CLIENT_ID, CLIENT_SECRET, "https://graph.microsoft.com/.default");
var params = { "$select": "onPremisesSecurityIdentifier,onPremisesSamAccountName,displayName" }
if (searchString) {
params["$search"] = quote('displayName:' + searchString);
}
var nextUrl = HTTP.url_encode("https://graph.microsoft.com/v1.0/groups", { }, params);
var fetchOptions = { headers: { Authorization: "Bearer " + accessToken, ConsistencyLevel: "eventual" } };
var result = [];
do {
var groups = JSON.parse(await HTTP.fetch(nextUrl, fetchOptions));
result.push(...groups.value.filter(g => g.onPremisesSamAccountName).map(g => ({ id: g.onPremisesSecurityIdentifier, name: domain_prefix + g.onPremisesSamAccountName, display_name: g.displayName })));
nextUrl = groups["@odata.nextLink"];
} while(nextUrl);
return result;
}
To use this script on your Azure AD tenant, you need to enable User.Read.All and Group.Read.All permissions on the app registration, so the app itself (using client ID and client secret) can get that information.
Comments
Please sign in to leave a comment.