Users

Last updated 3 months ago

A User is identified within mitter.io as an acting entity. Every action that is performed in an Application always has an associated User with it, and a User is referenced by a unique identifier. This identifier is customizable and can be overriden by the sender. The standard restrictions for an identifier apply as usual:

  1. The identifier can use alphanumeric characters.

  2. The identifier can use from a set of symbols (- _ @ $ #)

  3. The first character can only be alphanumeric, @ or #

  4. The identifier must be between 8 and 72 characters

This is the regex: [a-zA-Z0-9@#][a-zA-Z0-9-_@$#]*

Reserved and System Users

When any mitter.io API is called using an Application accesskey/token, the User id is set to .system. This user is an all-powerful user with the following (non-overridable) configuration:

  1. .system cannot send messages to, join, or remove itself from any channel.

  2. .system cannot login and cannot issue a token for itself.

When a mitter.io API is called without using any credentials, the user id is set to .anonymous. This user has absolutely no privileges and cannot call any API without it erroring.

The User Model

Here is the shape of the User model:

{
"userId": "XwS12-aG2F5-AAL23-1Kl7D",
"screenName": {
"screenName": "rylai"
},
"userLocators": ["email:crystalmaiden@gmail.com", "tele:+919876543210"],
"systemUser": false,
"audit": {
"createdAt": 1420070400,
"updatedAt": 0
}
}

Every User must have a screen name assigned to the object that could be used by clients to represent this user visually, but it is recommended that you either use User Profiles for this purpose. The screenName field is intended to be a visual aid for developmental purposes.

User Locators

A User Locator is a globally-unique identifier used to identify a User, for instance an 'email' or a 'phone number'. User locators are used as look-up keys for users across a variety of user-related APIs. Whenever expressing a user locator, it needs to be prefixed by the type of locator that is being used unless the user-locator is provided as an entity.

Currently mitter.io supports two types of locators:

  1. email (ex: email@mitter.io)

  2. tele (ex: +91-1234567890)

When using a user locator to make API calls, there are two ways it can be used:

  1. When it is passed as an object, in a request body. In this case, the structure is defined for each locator, and the first key of this strcture must be @type mentoning the type of locator it encodes (email or tele). The structure for an email is:

    {
    "@type": "email",
    "email": "test@domain.com",
    "verificationStatus": "VerificationPending"
    }

    and for a phone number:

    {
    "@type": "tele",
    "phoneNumber": "+915748395939",
    "verificationStatus": "VerificationPending"
    }
  2. When using it in a place where only strings are allowed, like a URI-part or a request parameter, it must follow the format @type:<serialized-format>. Each locator type defines its own serialized format. For the email locator, it is as is defined in the RFC 822 format and for a phone number it must be in the E164 format. Examples of this would be:

    email:test@domain.com tele:+911234567780

User Profile

Along with storing User Locators and their identifiers, mitter.io also supports storing of supplemental information regarding a User in User Profiles.

A User Profile, very simply put is a list of attributes and to each attribute an associated value. There are no restrictions on the name or type of the attribute, and they can be fixed depending upon the use case of your application. For instance, one would wish to store the 'First Name' and 'Last Name' of a user in their profile, so the two attributes the application could use is firstName and lastName.

All attributes that can be used in an application must be defined before using them, and all user profile attributes are available to all users across the Application. An attribute-def is made up of:

  1. Type - This identifies the the type of the attribute key. This is also equivalent to a "system name" for this attribute definition. In our example above, the type would be firstName.

  2. Allowed content types - A list of MIME types which are allowed as values against this attribute definition. In our example, we would only need text values, so we'll specify this as [text/plain].

  3. Allowed content encodings - Internally, and on an API level, only strings are supported, so if any binary data is to be stored (for instance an image avatar), then we need to use a content encoding to encode

    that data into a string. For an image avatar, that could be base64 as an example. In our case, we do not need to do any content encoding, so we'll just specify this as [identity].

  4. Can be empty - If an empty string (after truncating leading and trailing whitespace) can be set as a value against this attribute or not.

Our request to create a new attribute def would now look like:

POST /v1/attributedef
{
"type": "firstName",
"allowedContentTypes": ["text/plain"],
"canBeEmpty": false,
"allowedContentEncodings: ["identity"]
}

Similarly, we would create another attribute def with the type as lastName. Once these two attribute defs are created, users can not set their profile data. To set the users first name:

POST /v1/users/80947f0d-2f54-4d69-8990-e281d431eaca/profile/firstName
{
"contentType": "text/plain",
"contentEncoding": "identity",
"value": "Mirana"
}
POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/lastName
{
"contentType": "text/plain",
"contentEncoding": "identity",
"value": "Nightshade"
}

To fetch the profile for a user, a simple GET call would suffice:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/firstName
{
"contentType": "text/plain",
"contentEncoding": "identity",
"value": "Mirana"
}

However, when it comes to fetching a profile, it is usually the case to fetch the entire profile for display:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile
[
{
"type": "firstName",
"contentType": "text/plain",
"contentEncoding": "identity",
"value": "Mirana"
},
{
"type": "lastName",
"contentType": "text/plain",
"contentEncoding": "identity",
"value": "Nightshade"
}
]

or if you wish to fetch only a specific list of profile values:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/profile/firstName,lastName

Coming Soon We are extending the profile subsystem to other entities as well. In the first roll-out, Channels will also support storage of some supplemental data like the Channel name, a display icon etc.

User Presence

User presence is simply a short description on the availability of the User. Common presence values include 'Online', 'Away', 'Busy' etc. mitter.io allows presence to be set at a user level, and also specify an auto-expire for the presence at which point it can fallback to another presence. This is escpecially useful for a presence like 'Online', which if it hasn't been updated in the last certain time interval, must be auto-set to 'Away' (or some other status).

To set the presence for a user:

POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
"timeToLive": 0,
"type": "Away"
}

A timeToLive represents a static presence. This User's presence will now not be changed unless an explicit API call is made. To get the presence of a user:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
"type": "Away"
}

Let's take an example of a user whose presence is to be set to 'Online'. The client, whenever the user has the application opened, would make a call to the platform every 5 seconds, setting the user's profile as 'Online'. At the same time, if the application stops making this call, the presence should automatically fall back to 'Away'. To do this we can set a timeToLive and an expiresToPresence:

POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
"timeToLive": 10,
"type": Online,
"expiresTo": {
"timeToLive" 0,
"type": Offline
}
}

When the client closes the application, these persistent calls will no longer be made, and after 10 seconds since the last call, mitter.io will automatically set the user's presence to 'Offline. These expiresTo objects can be chained to any degree. For instance, if you wanted that when a user is inactive for 10 seconds, change the status to 'Away', if for 20, then change it to 'Inactive' and for any time more than 30, set it to 'Offline'

POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/presence
{
"timeToLive": 10,
"type": Online,
"expiresTo": {
"timeToLive": 10,
"type": "Away",
"expiresTo": {
"timeToLive": 10,
"type": "Inactive",
"expiresTo": {
"type": "Offline",
"timeToLive": 0
}
}
}
}

Do note that timeToLive for a presence is the time the presence will stay active from the time it is set. So in the above case, all the nested expiresTo call have a timeToLive of 10 seconds, since the time is counted from the instant they are set as the users active presence.

User Tokens and Authentication

A User is authenticated in an HTTP request by checking for an issued token. mitter.io issues revokable tokens to users which can be revoked at-will or expire after 24 hours of inactivity. The token is a signed JWT that merely holds the value of a token identifier held by mitter.io. A JWT token infrastructure is currently utilized for applications to provide a safe way to store certain verifiable user data while their users directly interact with mitter.io in future APIs.

A user token has the following components:

  1. A signed token - The actual token that must be provided to execute authenticated operations.

  2. A token id - This id can be used to revoke a token or for display/debugging purposes. Only an authenticated user can revoke a token with the token id, so this id is easily shareable.

  3. A TTL - A UNIX timestamp denoting the time when this token will expire (if the user were to perform no additional actions).

To access the mitter.io APIs, the following two headers need to be set:

X-Issued-Mitter-User-Authorization: <the-signed-token>
X-Mitter-Application-Id: <the-application-id>

User API Reference

The overall operations on a user can be categorized as:

  1. Operations on Users

  2. Managing User authentication and tokens

  3. Managing User metadata

Operations on Users

Creating a User

To create a user, we make a POST call to /v1/users. A user can only be created using an application access key/secret.

POST /v1/users
{
"screenName": {
"screenName": "rylai"
},
"userLocators": [
{
"@type": "email",
"email": "rylai@example.com"
}
]
}

And the response is a standard mitter.io identifier for the User:

200 OK
{
"userId": {
"identifier": "XwS12-aG2F5-AAL23-1Kl7D"
}
}

In the above example, we could also not pass any user locator. User locators can be added later as well. If a userId is not supplied, one will be generated.

Deleting a User

To delete a user make a DELETE call with the user-id in the URI. This API can be called only with a application access key/secret.

DELETE /v1/users/XwS12-aG2F5-AAL23-1Kl7D

The response being:

204 No Content

Fetching a User

By default, all methods of fetching a User can be performed by any authenticated User within the Application. This behavior can be modified using ACLs.

To fetch a user given an id, you can make a GET call as below:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D

This returns:

200 OK
{
"userId": "XwS12-aG2F5-AAL23-1Kl7D",
"userLocators": [ .. ],
..
}

NOTE All APIs above can also be called by an authenticated user to fetch details for themselves by using me as the user identifier. These API calls are not allowed for .system and .anonymous.

To fetch a list of users given a list of locators, make the same call but with the serialized locators passed in a CSV-format with the request parameter locators:

GET /v1/users?locators=email:rylai@example.com

This would fetch a respone similar to the above (it would return an array of User objects). To prevent dictionary-based scrubbing mechanisms to discover users, this API will not return any values if any of the passed locators do not match.

If your application is sandboxed, then you can also use your application credentials to get a list of sandboxed users. This API cannot be called by any other user, even in sandbox mode.

GET /v1/users?sandboxed=true

APIs for screen name of a user

To get the screen name of a user:

GET /v1/users/0b604184-7abd-453c-b258-7e4425b31e7f/screenname

The above API returns a list of screen names, as this API supports fetching the screen names for multiple users. Pass the user identifiers in a CSV format. The screen names are returned in the same order as the user identifiers passed in the request. An example response would be:

{
"screenNames": [
{
"screenName": "rylai"
}
]
}

To modify a screen name, make a PUT request:

PUT /v1/users/0b604184-7abd-453c-b258-7e4425b31e7f/screenname
{
"screenName": "crystal-maiden"
}

On successful execution, it returns:

204 No Content

Managing User Authentication and Tokens

All authenticated users can get additional tokens issued against them and revoke any tokens that are issued to them. The first initial token required for them to authenticate can only be issued by the .system user, i.e. using the application access key/secret.

No tokens can be issued for the reserved system users.

Currently, user tokens are valid for 24 hours, after which the User will need to re-authenticate, i.e., .system will have to issue a token. However, a User can extens the ttl before the expiry time.

Getting a New User Token

To get a new user token,

POST /v1/users/XwS12-aG2F5-AAL23-1Kl7D/tokens

Which returns:

{
"userToken": {
"signedToken": "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXI... truncated",
"supportedHeaders": [
"X-Issued-Mitter-User-Authorization"
],
"tokenId": "V4lKpqsbZK9C587R"
}
}

The signedToken is the token that is to be set as an HTTP header whenever making requests on behalf of this User. The name of the header(s) which can hold this value is provided in the supportedHeaders field. In addition to this, a tokenId is already returned which can be later used to revoke this user's tokens.

If a User is already authenticated, they can instead also use the following request to get additional tokens:

POST /v1/users/me/tokens

Revoking a User Token

To revoke a user token, make a DELETE call on the tokenId:

DELETE /v1/users/XwS12-aG2F5-AAL23-1Kl7D/V4lKpqsbZK9C587R

This operation can only be performed by the .system user, or by any other user for themselves.

If a user is already authenticated, they can instead also use:

DELETE /v1/users/me/V4lKpqsbZK9C587R

If the user wants to revoke the token they are currently authenticating with, they can use:

GET /v1/users/me/logout

Do note that this request uses the GET method and not the DELETE method so as to allow this to be a clickable link that can logout users that can be handled by browsers.

Listing All Tokens

To list all tokens that are issued to a user:

GET /v1/users/XwS12-aG2F5-AAL23-1Kl7D/tokens

This would return:

200 OK
[
{
"token": {
"token": "s3ee4bg1tctbj32igm15ipk0ta"
},
"ttl": 85962,
"tokenId": "V4lKpqsbZK9C587R"
}
]

Do note that the signedToken is not returned again. This is an API purely for reference purposes. The ttl gives the approximate number of seconds left before this token expires.

Any user can list their own tokens, and only the .system user can fetch tokens for other users. For an authenticated users, they could also:

GET /v1/users/me/tokens

This call is not permitted for .system or .anonymous.