Directory Agent Protocol
This document describes Nametag’s protocol for communicating with a directory agent. A directory agent implements the basic functions to interact with a directory service, such as listing accounts, groups, and users, and performing recovery operations on them. Nametag has implementations of the agent protocol for various directories (e.g. Okta, OneLogin, Duo, Microsoft Entra ID, etc.). You can also implement your own agent to interact with a directory service that is not supported by Nametag.
For more details on directory agents, refer to Directory Agent
Agent to server protocol
A directory agent communicates with the Nametag server via a WebSocket connection over
TLS. Implementations should connect to the Nametag server via WebSocket at
wss://nametag.co/api/diragent?auth=AGENT_TOKEN
.
The value for AGENT_TOKEN is provided in the output of the
nametag directory agent register
command. You must run this command prior to running a
directory agent.
Note: The AGENT_TOKEN is a least-privilege token that only allows for agent connection and nothing else. You must use an agent token to connect to this socket, you cannot use an API key. Once the agent is connected, the server sends WebSocket frames consisting of a JSON-encoded
DirAgentRequest
. The agent responds with a WebSocket frame consisting of a JSON-encodedDirAgentResponse
. The server may pipeline requests so that it sends a request before it receives a response from the prior request. The agent must take care to respond to requests in the same order they are received.
Agent to worker protocol
Nametag’s own implementation of the directory agent protocol is implemented as a main
process that handles authentication and the WebSocket connection, and an extensible worker
process that handles the actual directory operations. The worker process for official
agents is implemented as a separate subcommand of the nametag directory agent
command. You
are free to implement your own worker commands and run them with nametag directory agent
.
The main agent process communicates with the worker process by passing requests to the
worker’s standard input and reading responses from the worker’s standard output. Each
request is a JSON-encoded DirAgentRequest
, terminated by a newline (\n
). The worker
must respond with a JSON-encoded DirAgentResponse
terminated by a newline (\n
).
The agent may have multiple requests in flight at the same time. The worker must respond to messages in the same order they are received.
Protocol Overview
Each request sent to the agent is a JSON object with exactly one of following fields set:
DirAgentRequest struct | |
---|---|
configure DirAgentConfigureRequest
(optional) | This field is set when the request is to obtain configuration information about the directory agent. The server sends this request to determine the capabilities of the directory agent. |
list_accounts DirAgentListAccountsRequest
(optional) | This field is set when the server needs to obtain a list of accounts. The request contains parameters to filter and limit the accounts returned. |
get_account DirAgentGetAccountRequest
(optional) | This field is set when the server needs to get information about a specific account. |
list_groups DirAgentListGroupsRequest
(optional) | This field is set when the server needs to get a list of all possible groups. The request contains parameters to filter and limit the groups returned. |
perform_operation DirAgentPerformOperationRequest
(optional) | This field is set when the server needs to perform a recovery operation on an account, or with |
ping boolean (optional) | The server will periodically send a request with this field set to |
The response from the agent is a JSON object with exactly one following fields set:
DirAgentResponse struct | |
---|---|
configure DirAgentConfigureResponse
(optional) | This field should be set by the agent when the request has configure set to return the results of the request. If an error occurs, the agent should not set this field but should set error instead. |
list_accounts DirAgentListAccountsResponse
(optional) | This field should be set by the agent when the request has list_accounts set to return the results of the request. If an error occurs, the agent should not set this field but should set error instead. |
get_account DirAgentGetAccountResponse
(optional) | This field should be set by the agent when the request has get_account set to return the results of the request. If an error occurs, the agent should not set this field but should set error instead. |
list_groups DirAgentListGroupsResponse
(optional) | This field should be set by the agent when the request has list_groups set to return the results of the request. If an error occurs, the agent should not set this field but should set error instead. |
perform_operation DirAgentPerformOperationResponse
(optional) | This field should be set by the agent when the request has perform_operation set to return the results of the request. If an error occurs, the agent should not set this field but should set error instead. |
error DirAgentErrorResponse
(optional) | This field should be set by the agent when the request has failed. The code fields tells the server the general reason for the error. |
Error Handling
In the event of an error, the agent must respond with a DirAgentResponse
having the error field set.
DirAgentErrorResponse struct | |
---|---|
code DirAgentErrorCode
| An error code that describes the general reason for the error. |
message string | A human-readable message that describes the error in more detail. |
The code field must have one of the following values:
DirAgentErrorCode enum | |
---|---|
service_authentication_failed | The agent was unable to authenticate to the directory service. The administrator should be prompted to reconfigure authentication. |
permission_denied | In response to perform_operation, the agent has determined that the operation should not be allowed due to its own policy. |
account_not_found | The account specified in the request was not found. |
configuration_error | The agent is not configured correctly. The administrator should be prompted to reconfigure the agent. |
unsupported_account_state | In response to perform_operation, the agent has determined that the account is not in the proper state, e.g. attempting to unlock an account which is not locked. |
internal_error | The agent encountered an error in processing the request that does not fit into one of the other categories. |
Configure method
The server invokes this method to assess the agent’s capabilities. The agent should reply with the supported capabilities and, to the extent possible, confirm that its configuration is valid. For example, the agent could issue an innocuous API call to the directory service to verify that the configured credentials are valid.
Request:
DirAgentConfigureRequest struct |
---|
Response:
DirAgentConfigureResponse struct | |
---|---|
immutable_id string | Uniquely identifies the agent. |
traits DirAgentTraits
| Describes the capabilities of the directory agent. |
A Note on immutable_id:
The immutable_id returned as part of the configure response identifies an agent with the directory. It is up to the agent to generate a string that will tie the type of the underlying agent with the nametag custom directory. When an agent first connects to a new custom directory, the immutable_id that nametag receives as part of the configure response is associated with the directory. Subsequently, only agents that return that immutable_id will be allowed to connect to that custom directory.
The traits field contains the following fields:
DirAgentTraits struct | |
---|---|
name string | The display name of the directory agent. This is the name that will be displayed to administrators and end-users in the user interface, for example "Okta" or "ExampleCorp". |
can_get_temporary_password boolean (optional) | Indicates whether the agent can create a temporary password for an account, allowing the user to log in for a limited time. |
can_get_password_link boolean (optional) | Indicates whether the agent can generate a pre-authenticated link that leads the user to a site (typically provided by the directory service) that the user can use enter a new password. |
can_remove_all_mfa boolean (optional) | Indicates whether the agent can remove all MFA factors from an account so the user can re-enroll their MFA device. |
can_get_mfa_bypass_code boolean (optional) | Indicates whether the agent can generate a bypass code that the user can use to sign in in place of their MFA device. Typically after using a bypass code the user will be able to enroll a replacement MFA device. |
can_unlock boolean (optional) | Indicates whether the agent can unlock an account that has been locked due to too many failed login attempts. |
can_get_temporary_access_pass boolean (optional) | Indicates whether the agent can generate a temporary code that the user will use to log in temporarily without revoking their existing password or resetting any MFA devices. |
can_update_accounts_list boolean (optional) | Indicates whether the directory service supports tracking the last modification time of the account list. If supported, the server may set updated_after in list_accounts to indicate the agent should report only accounts that have been updated since that time. |
ListAccounts method
The server calls this method to determine which accounts are present in the directory. Because directories can contain thousands of accounts, the agent return accounts in pages. The server will call this method multiple times to retrieve all accounts. The cursor field is used to track the progress through the list of accounts.
There is no hard limit on the number of accounts that can be returned in each page, but the agent should return a reasonable number of accounts to avoid reliability problems. Many directory services themselves paginate the list of accounts and have recommended page sizes, which can be used as the page size when implementing this method. Nametag’s own implementations of this protocol returns at most 250 accounts per page, but the exact appropriate page size may depend on the directory service being used.
If the agent supports the can_update_accounts_list trait, the server may set updated_after to a timestamp. The agent should return only accounts that have been updated after this timestamp. While iterating the list of accounts using cursor, the server will set the same updated_after for each request.
Request:
DirAgentListAccountsRequest struct | |
---|---|
updated_after RFC 3339 date-time string (optional) | Only return accounts that have been updated after this time. |
cursor string (optional) | If present, the server is requesting a continuation of a previous list of accounts. The value will be the one most recently returned in the next_cursor field. The format of this value is determined by the agent. The server treats this value as opaque and passes it back to the agent in the next request. To start at the beginning of the list, the server will omit this field. |
Response:
DirAgentListAccountsResponse struct | |
---|---|
accounts List of DirAgentAccount
| The accounts that the agent has discovered. |
next_cursor string (optional) | If there are more results to return, the agent should set this field to the value that should be passed back to the server in the next request. If there are no more results, the agent should omit this field. |
Each item in the accounts field is a DirAgentAccount
object:
DirAgentAccount struct | |
---|---|
immutable_id string | A unique identifier for the account that will not change over time. In many directory services this is an UUID or similar opaque identifier. |
ids List of string | A list of account identifiers, such as email addresses or usernames. These identifiers are used to select accounts for various operations. Many directory services have multiple identifiers for an account, such as alternate email addresses and aliases. Each identifier that might be reasonably used to identify an account should be placed here. |
name string | The name of the person that holds the account. This is the value that Nametag uses to match against the person's verified government ID. This is the name that will be displayed to administrators and end-users in the user interface. |
groups List of DirAgentGroup
(optional) | A list of groups that this account belongs to. |
birth_date string (optional) | If the directory service has a birth date for the account, it should be included here. This is used to match against the person's verified government ID. The birth date can take various forms, including a RFC 3339 date string (YYYY-MM-DD), or a specially constructed hash. |
updated_at RFC 3339 date-time string (optional) | The time when this account, or its group membership, was last modified. This field is required if can_update_accounts_list is set to |
The groups field contains a list of groups that an account is a member of.
DirAgentGroup struct | |
---|---|
immutable_id string | An identifier for the group that will not change over time. In many directory services this is an UUID or similar opaque identifier. If the directory service doesn't distinguish between group identifiers and names, use the same value here as for name. |
name string | The group's display name. |
kind string | Some directory services have different kinds ways of grouping users, each of which can be considered a group for Nametag's purposes. For example, you might have "security groups", "roles", "administrative units", etc. This field should be set to the kind of group that this is. The semantics of this field are up to the agent. Nametag treats it as opaque. |
GetAccount method
The server calls this method to retrieve the details of a specific account.
Request:
DirAgentGetAccountRequest struct | |
---|---|
ref DirAgentAccountRef
| Specifies which account to fetch information about. |
A DirAgentAccountRef object is a reference to an account. Exactly one of the fields in this object must be specified.
DirAgentAccountRef struct | |
---|---|
immutable_id string (optional) | The immutable identifier of the account to fetch information about. |
id string (optional) | One of the identifiers of the account to fetch information about. This is typically an email address or username. |
Response:
DirAgentGetAccountResponse struct | |
---|---|
accounts List of DirAgentAccount
| Information about the accounts that match the specified reference. If you specified an immutable_id in the request, the response will contain at most one account. If you specified an id in the request, because the same id can be present on multiple accounts, the response may contain multiple accounts. Unlike the list_accounts method, the accounts in this list should contain groups information, even if it requires the agent to perform additional work to determine group membership. If no accounts match, this list should be empty. |
If no accounts match the specification, the agent must respond with an empty list, and must not set an error code.
ListGroups method
The server calls this method to determine which groups are present in the directory. Because directories can contain thousands of groups, the agent return groups in pages. The server will call this method multiple times to retrieve all groups. The cursor field is used to track the progress through the list of accounts.
There is no hard limit on the number of groups that can be returned in each page, but the agent should return a reasonable number of groups to avoid reliability problems. Many directory services themselves paginate the list of groups and have recommended page sizes, which can be used as the page size when implementing this method. Nametag’s own implementations of this protocol return at most 250 groups per page, but the exact appropriate page size may depend on the directory service being used.
Request:
DirAgentListGroupsRequest struct | |
---|---|
name_prefix string (optional) | The agent should return only groups whose name starts with this prefix. |
max_count integer (optional) | The maximum number of groups to return. If the agent has more groups than this, it should return the first max_count groups in lexicographic order by name. Note: this is not the maximum number of groups per page, this is simply a signal that the server will discard any groups beyond this number. |
cursor string (optional) | If present, the server is requesting a continuation of a previous list of groups. The value will be the one most recently returned in the next_cursor field. The format of this value is determined by the agent. The server treats this value as opaque and passes it back to the agent in the next request. To start at the beginning of the list, the server will omit this field. |
Response:
DirAgentListGroupsResponse struct | |
---|---|
groups List of DirAgentGroup
| The groups that the agent has discovered. |
next_cursor string (optional) | If there are more results to return, the agent should set this field to the value that should be passed back to the server in the next request. If there are no more results, the agent should omit this field. |
PerformOperation method
The server calls this method to perform a recovery operation (e.g. resetting a password) on an account, or to test if an account is eligible for an operation. The agent should respond with the results of the operation, or an error if the operation cannot be performed.
Request:
DirAgentPerformOperationRequest struct | |
---|---|
operation DirAgentOperation
| The operation to perform on the account. |
account_immutable_id string | The immutable identifier of the account to perform the operation on. |
dry_run boolean (optional) | If set to |
The operation field specifies the operation to perform. It has one of the following values:
DirAgentOperation enum | |
---|---|
get_temporary_password | Generate a temporary password for the account. |
get_password_link | Generate a pre-authenticated link that leads the user to a site where they can enter a new password. |
remove_all_mfa | Remove all MFA factors from the account to permit a user to re-enroll in MFA. |
get_mfa_bypass_code | Generate a bypass code that the user can use to sign in in place of their MFA device. |
unlock | Unlock the account that has been locked due to too many failed login attempts. |
get_temporary_access_pass | Generate a temporary access pass for the account. |
Response:
DirAgentPerformOperationResponse struct | |
---|---|
temporary_password string (optional) | If the operation was get_temporary_password, this field should contain the temporary password that the user can use to log in and set a new password. |
password_link string (optional) | If the operation was get_password_link, this field should contain a pre-authenticated link that the user can use to set a new password. |
mfa_bypass_code string (optional) | If the operation was get_mfa_bypass_code, this field should contain the bypass code that the user can use to sign in in place of their MFA device. |