Skip to content

Commit

Permalink
Merge pull request #2119 from ably/EDU-362-update-docs-js-code-sample…
Browse files Browse the repository at this point in the history
…s-to-use-promise-based-sdk-functionality

[EDU-362] - Update Channels docs Javascript and NodeJS examples to use Promise based SDK functionality
  • Loading branch information
GregHolmes authored Mar 13, 2024
2 parents e0d740b + c4c6aa8 commit 440cc5d
Show file tree
Hide file tree
Showing 22 changed files with 708 additions and 650 deletions.
8 changes: 6 additions & 2 deletions content/auth/basic.textile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ Basic authentication is the simplest way to authenticate with Ably. It requires
The following is an example of using basic authentication:

```[realtime_javascript]
var realtime = new Ably.Realtime({ key: '{{API_KEY}}' });
const realtime = new Ably.Realtime.Promise({
key: '{{API_KEY}}'
});
```

```[realtime_nodejs]
var realtime = new Ably.Realtime({ key: '{{API_KEY}}' });
const realtime = new Ably.Realtime.Promise({
key: '{{API_KEY}}'
});
```

```[realtime_ruby]
Expand Down
139 changes: 68 additions & 71 deletions content/auth/capabilities.textile
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ languages:
- javascript
---

API keys and Ably-compatible tokens, have a set of capabilities assigned to them that specify which operations (such as subscribe or publish) can be performed on which channels.
API keys and Ably-compatible tokens, have a set of capabilities assigned to them that specify which operations (such as subscribe or publish) can be performed on which channels.

API keys are long-lived, secret and typically not shared with clients. API key capabilities are configured using the "dashboard":https://ably.com/dashboard, or using the "Control API":/account/control-api.
API keys are long-lived, secret and typically not shared with clients. API key capabilities are configured using the "dashboard":https://ably.com/dashboard, or using the "Control API":/account/control-api.

Ably-compatible tokens are designed to be shared with untrusted clients, are short-lived, and can be configured and issued programmatically. See "selecting an authentication mechanism":/auth#selecting-auth to understand why token authentication is the preferred option in most scenarios.

h2(#wildcards). Resource names and wildcards

Capabilities are a map from resources to a list of "operations":#capability-operations. Each resource can match a single channel, for example, @channel@, or multiple channels using wildcards (@*@).
Capabilities are a map from resources to a list of "operations":#capability-operations. Each resource can match a single channel, for example, @channel@, or multiple channels using wildcards (@*@).

Wildcards can only replace whole segments (segments are delimited by @:@) of the resource name. A wildcard at the end of the name can arbitrarily replace many segments. For example:

Expand Down Expand Up @@ -45,7 +45,7 @@ The following capability operations are available for API keys and issued tokens
- channel-metadata := can get metadata for a channel, and enumerate channels
- privileged-headers := can set data in the privileged section of the "message extras":/api/realtime-sdk/messages#extras

Although most capabilities need to be enabled for the resource you're using them with, there are exceptions. The @stats@ permission only does something when attached to the wildcard resource @'*'@, or a resource that contains that as a subset, such as @'[*]*'@, since stats are app-wide.
Although most capabilities need to be enabled for the resource you're using them with, there are exceptions. The @stats@ permission only does something when attached to the wildcard resource @'*'@, or a resource that contains that as a subset, such as @'[*]*'@, since stats are app-wide.

The @channel-metadata@ permission works both ways. When associated with a specific channel or set of channels it allows you to "query the metadata of a channel":/metadata-stats/metadata/rest to request its status. When associated with the wildcard resource @'*'@ it takes on an additional meaning: as well as allowing channel status requests for all channels, it also allows you to "enumerate all active channels":/metadata-stats/metadata/rest#enumerate.

Expand All @@ -69,18 +69,15 @@ To view the capabilities for an existing API key:

h2(#capabilities-token). Token capabilities

Ably Tokens and JWTs are issued from an existing API key and their capabilities can, at most, match the capabilities of the issuing API key.
Ably Tokens and JWTs are issued from an existing API key and their capabilities can, at most, match the capabilities of the issuing API key.

If an API key must be shared with a third party, then it is recommended that "the principle of least privilege":https://en.wikipedia.org/wiki/Principle_of_least_privilege is considered, assigning only the capabilities needed by that third party. Thus, any Ably requests authenticated using that API key or Ably-compatible tokens associated with that API key, will be restricted to the capabilities assigned to the API key.

Capabilities can be set when creating a token or token request, as shown in the following example:

```[javascript]
var tokenParams = { clientId: 'foo', capability: JSON.stringify(capability) };
ably.auth.createTokenRequest(tokenParams, (err, token) => {
// handle error or
// return token request
});
const tokenRequest = await ably.auth.createTokenRequest(tokenParams);
```

h3(#capability-determination). Token capability determination
Expand All @@ -94,20 +91,20 @@ If no capability is specified in an Ably @TokenRequest@, then the "Ably Token":/
Using the following example, an API key exists with the listed capabilities. If an Ably Token is requested without specifying any capabilities then the @TokenRequest@ is treated as requesting all capabilities, i.e. @{"[*]*":["*"]}@). This will result in the Ably Token receiving all the capabilities of the API key.

```[javascript]
// API key capabilities:
{
"chat": ["publish", "subscribe", "presence"],
"status": ["subscribe"]
}

// Token request that doesn't specify any capabilities:
auth.requestToken(tokenCallback)

// Resulting token capabilities:
{
"chat": ["publish", "subscribe", "presence"],
"status": ["subscribe"]
}
// API key capabilities:
{
'chat': ['publish', 'subscribe', 'presence'],
'status': ['subscribe']
}

// Token request that doesn't specify any capabilities:
await auth.requestToken(tokenCallback)

// Resulting token capabilities:
{
'chat': ['publish', 'subscribe', 'presence'],
'status': ['subscribe']
}
```

h4(#ably-token-intersection). Ably Token with intersection of capabilities
Expand All @@ -117,25 +114,25 @@ If a set of capabilities are requested, then the Ably Token will be assigned the
Using the following example, an API key exists with the listed capabilities. If an "Ably Token":/auth/token#tokens is requested and specifies a set of capabilities, then the resulting token will only receive those capabilities that intersect. The capabilities of a token cannot exceed those of the issuing API key.

```[javascript]
// API key capabilities:
{
"chat:*": ["publish", "subscribe", "presence"],
"status": ["subscribe", "history"],
"alerts": ["subscribe"]
}

// Token request that specifies capabilities:
auth.requestToken({ capability: {
"chat:bob": ["subscribe"], // only "subscribe" intersects
"status": ["*"], // "*"" intersects with "subscribe"
"secret": ["publish", "subscribe"] // key does not have access to "secret" channel
}}, tokenCallback)

// Resulting token capabilities:
{
"chat:bob": ["subscribe"],
"status": ["subscribe", "history"]
}
// API key capabilities:
{
'chat:*': ['publish', 'subscribe', 'presence'],
'status': ['subscribe', 'history'],
'alerts': ['subscribe']
}

// Token request that specifies capabilities:
const tokenDetails = await auth.requestToken({ capability: {
'chat:bob': ['subscribe'], // only 'subscribe' intersects
'status': ['*'], // '*'' intersects with 'subscribe'
'secret': ['publish', 'subscribe'] // key does not have access to 'secret' channel
}});

// Resulting token capabilities:
{
'chat:bob': ['subscribe'],
'status': ['subscribe', 'history']
}
```

h4(#ably-token-error). Ably Token with incompatible capabilities
Expand All @@ -145,15 +142,15 @@ If a set of capabilities are requested, and the intersection between those and t
Using the following example, an API key exists with the listed capabilities. If an "Ably Token":/auth/token#tokens is requested that specifies a set of capabilities, and there is no intersection between the capabilities of the issuing API key and requested token, then the token request will be rejected. In the following example, the callback will be returned with an error.

```[javascript]
// API key capabilities:
{
"chat": ["*"]
}

// Token request that specifies capabilities:
auth.requestToken({ capability: {
"status": ["*"]
}}, tokenCallback)
// API key capabilities:
{
'chat': ['*']
}

// Token request that specifies capabilities:
const tokenDetails = await auth.requestToken({ capability: {
'status': ['*']
}});
```

h4(#ably-jwt). Ably JWT capability determination
Expand All @@ -174,24 +171,24 @@ An example use case is when using "message interactions":/channels/messages#inte
To set the trusted fields you need to include @ably.channel.*@ in your JWT authentication payload, for example:

```[javascript]
const claims = {
"sub": "1234567890",
"name": "John Doe",
"x-ably-capability": <...>,
"x-ably-clientId": <...>,
"ably.channel.chat1": "admin", // the user is an admin for the chat1 channel
"ably.channel.chat:*": "moderator", // the user is a moderator in channels within the chat namespace
"ably.channel.*": "guest", // the user is a guest in all other channels
}
const claims = {
'sub': '1234567890',
'name': 'John Doe',
'x-ably-capability': <...>,
'x-ably-clientId': <...>,
'ably.channel.chat1': 'admin', // the user is an admin for the chat1 channel
'ably.channel.chat:*': 'moderator', // the user is a moderator in channels within the chat namespace
'ably.channel.*': 'guest', // the user is a guest in all other channels
}
```

The claims from the token are copied into messages, allowing them to be checked for permission:

```[javascript]
const fromModerator = (message) => {
const fromModerator = (message) => {
const userClaim = message.extras && message.extras.userClaim;
return (userClaim && userClaim == 'moderator');
}
}
```

h2(#jwt-limits). Using JWT for per connection publish rate limits
Expand All @@ -200,19 +197,19 @@ JWTs may specify publish rate limits for a user on particular channels. These li

An example use case is in a large live chat where you may wish to limit users to posting messages no more than once every 10 seconds.

Rate limits can be scoped to individual channels or to "channel namespaces":/channels#namespaces. Note that the rate limit with the most specific scope will be applied to the user.
Rate limits can be scoped to individual channels or to "channel namespaces":/channels#namespaces. Note that the rate limit with the most specific scope will be applied to the user.

To set rate limits for individual connections, include @ably.limits.publish.perAttachment.maxRate.<resource-name>@ in your JWT authentication payload. The value of this property sets how many messages can be published per second to a channel, or namespace. For example, a value of @5@ restricts the rate to 5 messages per second. A value of @0.1@ restricts the rate to 1 message every 10 seconds.

The following is an example of setting different rate limits for different channels:

```[javascript]
const claims = {
"sub": "1234567890",
"name": "John Doe",
"x-ably-capability": <...>,
"x-ably-clientId": <...>,
"ably.limits.publish.perAttachment.maxRate.chat1": 10, // the user can publish 10 messages per second in channel chat1
"ably.limits.publish.perAttachment.maxRate.chat:*": 0.1 // the user can publish a message every 10 seconds in all channels within the chat namespace
}
const claims = {
'sub': '1234567890',
'name': 'John Doe',
'x-ably-capability': <...>,
'x-ably-clientId': <...>,
'ably.limits.publish.perAttachment.maxRate.chat1': 10, // the user can publish 10 messages per second in channel chat1
'ably.limits.publish.perAttachment.maxRate.chat:*': 0.1 // the user can publish a message every 10 seconds in all channels within the chat namespace
}
```
24 changes: 8 additions & 16 deletions content/auth/identified-clients.textile
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,13 @@ For example, when publishing a message, the @clientId@ attribute of the message
The following example demonstrates how to issue an "Ably TokenRequest":/auth/token#token-request with an explicit @clientId@:

```[realtime_javascript]
var realtime = new Ably.Realtime({ key: '{{API_KEY}}' });
realtime.auth.createTokenRequest({ clientId: 'Bob' }, function(err, tokenRequest) {
/* ... issue the TokenRequest to a client ... */
})
const realtime = new Ably.Realtime.Promise({ key: '{{API_KEY}}' });
const tokenRequest = await realtime.auth.createTokenRequest({ clientId: 'Bob'});
```

```[realtime_nodejs]
var realtime = new Ably.Realtime({ key: '{{API_KEY}}' });
realtime.auth.createTokenRequest({ clientId: 'Bob' }, function(err, tokenRequest) {
/* ... issue the TokenRequest to a client ... */
})
const realtime = new Ably.Realtime.Promise({ key: '{{API_KEY}}' });
const tokenRequest = await realtime.auth.createTokenRequest({ clientId: 'Bob'});
```

```[realtime_ruby]
Expand Down Expand Up @@ -110,17 +106,13 @@ The following example demonstrates how to issue an "Ably TokenRequest":/auth/tok
```

```[rest_javascript]
var rest = new Ably.Rest({ key: '{{API_KEY}}' });
rest.auth.createTokenRequest({ clientId: 'Bob' }, function(err, tokenRequest) {
/* ... issue the TokenRequest to a client ... */
})
const rest = new Ably.Rest.Promise({ key: '{{API_KEY}}' });
const tokenRequest = await realtime.auth.createTokenRequest({ clientId: 'Bob'});
```

```[rest_nodejs]
var rest = new Ably.Rest({ key: '{{API_KEY}}' });
rest.auth.createTokenRequest({ clientId: 'Bob' }, function(err, tokenRequest) {
/* ... issue the TokenRequest to a client ... */
})
const rest = new Ably.Rest.Promise({ key: '{{API_KEY}}' });
const tokenRequest = await realtime.auth.createTokenRequest({ clientId: 'Bob'});
```

```[rest_ruby]
Expand Down
46 changes: 25 additions & 21 deletions content/auth/revocation.textile
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,21 @@ A sample request to revoke an Ably token based on @clientId@, using the REST int
```[rest_javascript]
const Ably = require('ably');

const ablyRest = new Ably.Rest({ key: '{{API_KEY}}' });
const ablyRest = new Ably.Rest.Promise({ key: '{{API_KEY}}' });
const requestBody = { targets: ['clientId:[email protected]'] };

ablyRest.request('post', '/keys/{{API_KEY_NAME}}/revokeTokens', null, requestBody, null,

function (err, response) {
console.log(response);
if (!response.success) {
console.log('An error occurred; err = ' + response.errorMessage);
} else {
console.log('Success! status code was ' + response.statusCode);
}
}
const revocationResponse = await ablyRest.request(
'post',
'/keys/{{API_KEY_NAME}}/revokeTokens',
null,
requestBody
);

if (!revocationResponse.success) {
console.log('An error occurred; err = ' + revocationResponse.errorMessage);
} else {
console.log('Success! status code was ' + revocationResponse.statusCode);
}
```

In this example, the token with the unique client ID <code>[email protected]</code> would be revoked.
Expand All @@ -128,18 +129,21 @@ A sample request to revoke a JWT based on @revocationKey@, using the REST interf
```[rest_javascript]
const Ably = require('ably');

const ablyRest = new Ably.Rest({ key: '{{API_KEY}}' })
const ablyRest = new Ably.Rest.Promise({ key: '{{API_KEY}}' })
const requestBody = { targets: ['revocationKey:[email protected]'] };
ablyRest.request('post', '/keys/{{API_KEY_NAME}}/revokeTokens', null, requestBody, null,
function (err, response) {
console.log(response);
if (!response.success) {
console.log('An error occurred; err = ' + response.errorMessage);
} else {
console.log('Success! status code was ' + response.statusCode);
}
}
const revocationResponse = await ablyRest.request(
'post',
'/keys/{{API_KEY_NAME}}/revokeTokens',
null,
requestBody,
null
);

if (!revocationResponse.success) {
console.log('An error occurred; err = ' + revocationResponse.errorMessage);
} else {
console.log('Success! status code was ' + revocationResponse.statusCode);
}
```

In this example, all users that have been assigned the revocation key <code>[email protected]</code> would have their tokens revoked.
Expand Down
Loading

0 comments on commit 440cc5d

Please sign in to comment.