---
source_url: https://www.pubnub.com/docs/general/resources/migration-guides/pam-v3-migration
title: Access Manager v3 Migration Guide
updated_at: 2026-06-04T11:10:29.643Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Access Manager v3 Migration Guide

Access Manager v3 is a cryptographic, token‑based permission system that lets you regulate client access to PubNub resources, such as channels, channel groups, and User IDs. Access Manager helps you increase security and prevent access without explicit permission. This guide is for developers using Access Manager v2.

:::note User ID / UUID
User ID is also referred to as **UUID/uuid** in some APIs and server responses but **holds the value** of the **userId** parameter you [set during initialization](https://www.pubnub.com/docs/general/setup/users-and-devices#set-the-user-id).
:::

The v3 model lowers latency and improves stability. Complexity moves from authorization to grant requests. Tokens returned by the grant API work immediately. Clients are less likely to see latency on API calls. v3 adds security options and more flexible permission checks. See the [Access Manager v3 feature description](https://www.pubnub.com/docs/general/security/access-control).

If your app uses v2 today, PubNub continues to process requests. We recommend migrating to v3. This guide summarizes the differences, compares data flows, and outlines migration steps.

## Differences between v2 and v3

See all the major differences between the two versions:

| Feature | Access Manager v2 | Access Manager v3 |
| --- | --- | --- |
| Authentication method | Authentication key generated by the client. | Token generated by PubNub upon a [grant request](https://www.pubnub.com/docs/general/security/access-control#grant-permissions) made by the server. |
| Permissions storage | Permissions are stored as an access control list (ACL) in the database on the PubNub server. | Permissions are embedded in a token (self-contained). |
| Permissions expiration | Each resource has its own unique `ttl` (time to live). | There is one `ttl` that's set at a token level. |
| Authorization check latency | Database lookup is required and that increases latency. | Instant check as permissions are embedded in the token. |
| Grant latency | High latency | Low latency - clients can connect immediately after they receive tokens. |
| Pattern-based permissions | It supports only one-level wildcard notations (`a.*`) for the `channel` resource. | It supports RegEx for `channels`, `groups` (channel groups), and `uuids`. |
| Multiple permission grants | You must make separate API calls for multiple permission sets. | You can make a single API call to define multiple permission sets for a given authorized User ID. |

## Differences in data flows

Typical PubNub solutions that use Access Manager include a centralized, customer-developed server application that initializes a PubNub SDK with a `secretKey` allowing the server to make calls to the PubNub grant API. Clients connect with this server through application-specific interfaces developed by the customer. The following descriptions show examples of client-server interactions initiated by a user logging in to the client device. The idea behind it is to demonstrate how permissions are governed and distributed in both Access Manager versions, and how these flows differ.

### Access Manager v2

A client will typically log in to a server application that first identifies the set of permissions associated with that client's user and later creates an `authKey` for it. The `authKey` and a defined permission set are sent to PubNub by the server in a grant call. PubNub stores the permissions mapping for the provided `authKey` in a database. Your client device passes this `authKey` during PubNub object initialization and retains it across multiple API calls. When the `authKey` expires and the client device attempts to log in again, it will have to request to be re-granted further access.

![Access Manager v2 flow: client logs in to your server; server grants authKey; client uses authKey with PubNub](https://www.pubnub.com/assets/images/pam-v2-authorization-flow-17bc1062413d4d0b87ae8eda6498019d.png)

### Access Manager v3

A client connects to the server application to initialize itself. That typically happens during a login attempt. Depending on your application logic, the client may present the permissions it needs, and your server will have a way of authenticating that this request is coming from an approved client. Alternatively, your client will be providing some type of identity request and your server will determine the appropriate permissions for that user. In either case, the server makes a grant request to PubNub to get a token with these permissions. In this grant request, the server defines how long these permissions should be valid. In turn, PubNub generates a time-limited, signed token with embedded permissions and returns the token to the server. The server passes this token back to the client device so it can communicate directly with PubNub. When the client device wants to change the permissions, the token is about to expire, or has been revoked, the client calls the server to request PubNub to [grant a new token](https://www.pubnub.com/docs/general/security/access-control#request-a-new-token). Once the server receives a new one, it passes the token back to the client device.

![Access Manager v3 flow: client logs in; server requests token via grant; client uses token with PubNub](https://www.pubnub.com/assets/images/pam-v3-authorization-flow-6bf6d61d27e7875d1feef624104c6434.png)

:::note Token size limits
There's a 32 KiB limit for the overall size of an HTTP request including a token. To avoid errors due to excessively large tokens in requests, set permissions with RegEx and use consistent naming conventions for your resources. See the [channel naming convention](https://www.pubnub.com/docs/general/channels/channel-naming#recommended-naming-convention) for reference, or [contact support](https://www.pubnub.com/docs/mailto:support@pubnub.com) for any questions.
:::

## Migration steps

To migrate from Access Manager v2 to v3:

1. Remove `authKey` from your client-side configuration and prepare the logic to set the token (which is returned by your server):

### Node.js

```javascript
const pubnub = new PubNub({
  subscribeKey: "mySubscribeKey",
  publishKey: "myPublishKey",
  userId: "myUniqueUserId"
});

pubnub.setToken("yourToken"); // Set the token returned by the server (see the next step for more details)
```

### Python

```python
pn_config = PNConfiguration()
pn_config.subscribe_key = "my_subscribe_key"
pn_config.publish_key = "my_publish_key"
pn_config.user_id = "my_unique_user_id"
pubnub = PubNub(pn_config)

pubnub.set_token("your_token") # Set the token returned by the server (see the next step for more details)
```

### Java

```java
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
configBuilder.publishKey("myPublishKey");
PubNub pubNub = PubNub.create(configBuilder.build());

pubNub.setToken("yourToken");
// Set the token returned by the server (see the next step for more details)
```

### Kotlin

```kotlin
val pnConfiguration = PNConfiguration(UserId("myUserId")).apply {
  subscribeKey = "my_subkey"
  publishKey = "my_pubkey"
  secure = true
}
val pubnub = PubNub.create(pnConfiguration)

pubnub.setToken("yourToken"); // Set the token returned by the server (see the next step for more details)
```

### Go

```go
pnconfig := pubnub.NewConfig()
pnconfig.SubscribeKey = "MySubscribeKey"
pnconfig.PublishKey = "MyPublishKey"
pnconfig.SetUserId(UserId("myUniqueUserId"))
pn := pubnub.NewPubNub(pnconfig)

pn.SetToken("NewToken") // Set the token returned by the server (see the next step for more details)
```

### C#

```csharp
PNConfiguration pnconfig = new PNConfiguration();
pnconfig.SubscribeKey = "mySubscribeKey";
pnconfig.PublishKey = "myPublishKey";
pnconfig.UserId = "MyUniqueUserId";
Pubnub pubnub = new Pubnub(pnconfig);

pubnub.SetAuthToken("NewToken") // Set the token returned by the server (see the next step for more details)
```

### Dart

```dart
final myKeyset = Keyset(
subscribeKey: 'mySubscribeKey',
publishKey: 'myPublishKey',
userId: UserId('yourUniqueUserId')
);

pubnub.setToken("yourToken") // The token is returned by the server in the next step
```

### PHP

```php
use PubNub\PNConfiguration;
$pnConfiguration = new PNConfiguration();
$pnConfiguration->setSubscribeKey("MySubscribeKey");
$pnConfiguration->setPublishKey("MyPublishKey");
$pnConfiguration->setUuid("MyUniqueUuid");

$pubnub->setToken("NewToken");
```

### Ruby

```ruby
pubnub = Pubnub.new(
    subscribe_key: 'my_subscribe_key',
    publish_key: 'my_publish_key',
    user_id: 'myUniqueUserId'
);

pubnub.set_token("newToken"); # Set the token returned by the server (see the next step for more details)
```

Tokens may need to be updated for various reasons. One reason could be because the user’s permissions have changed. Applications that securely authenticate client connections may allow clients to request changes to their own permissions. Other architectures may initiate changes on the server side and notify clients that they need a new token. Regardless of the approach, server logic should provide an interface that clients can call to request a new token.

Clients may also need to request a new token if it has (or will soon be) expired. Clients that make PubNub API requests with an expired token will get a `403` response.

Regardless of why a client has requested a new token, the server logic should make a new grant request and return the new token to the client. Once clients have the new token, they can set it in their PubNub object the same way they set a brand new token. Make sure not to create a new PubNub object on every token update, but rather set the token using the `set_token()` method (name may vary) available in your SDK.

For more information, refer to [Manage Permissions with Access Manager](https://www.pubnub.com/docs/general/security/access-control#request-a-new-token).

1. Update your server-side code around grant APIs:

#### Node.js

```javascript
const pubnub = new PubNub({
    subscribeKey: "mySubscribeKey",
    publishKey: "myPublishKey",
    secretKey: "mySecretKey",
    userId: "myUniqueUserId"
  });

pubnub.grantToken(
  {
    ttl: 15,
    authorized_uuid: "myAuthorizedUUID",
    resources: {
      channels: {
        "myChannel": {
          read: true  // False to disallow
        }
      }
    }
  }, function(status, token) {
    console.log(token);
  // Provide logic to return this token to your client
  }
);
```

#### Python

```python
from pubnub.models.consumer.v3.channel import Channel
from pubnub.models.consumer.v3.group import Group
from pubnub.models.consumer.v3.uuid import UUID

pn_config = PNConfiguration()
pn_config.subscribe_key = "my_subscribe_key"
pn_config.publish_key = "my_publish_key"
pn_config.secret_key = "my_secret_key"
pn_config.user_id = "my_unique_user_id"
pubnub = PubNub(pn_config)

envelope = pubnub.grant_token() \
    .ttl(60) \
    .authorized_uuid('some_uuid') \
    .channels([
        Channel().id('some_channel_id').read().write().manage().delete().get().update().join(),
        Channel().pattern('some_*').read().write().manage()
    ]) \
    .groups([
        Group().id('some_group_id').read().manage(),
        Group().pattern('some_*').read(),
    ]) \
    .uuids([
        UUID().id('some_uuid').get().update().delete(),
        UUID().pattern('some_*').get()
    ]) \
    .sync()

token = envelope.result.token
# Provide logic to return this token to your client
```

#### Java

```java
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
// publishKey from Admin Portal (only required if publishing)
configBuilder.publishKey("PublishKey");
configBuilder.secretKey("mySecretKey");
PubNub pubNub = PubNub.create(configBuilder.build());

pubNub.grantToken()
    .ttl(15)
    .authorizedUUID("myAuthorizedUUID")
    .channels(Arrays.asList(ChannelGrant.name("myChannel").read()
    .async(result -> { /* check result */ });
```

#### Kotlin

```kotlin
val pnConfiguration = PNConfiguration(UserId("myUserId")).apply {
    subscribeKey = "my_subkey"
    publishKey = "my_pubkey"
    secretKey = "my_secretkey"
    secure = true
  }
val pubnub = PubNub.create(pnConfiguration)
pubnub.grantToken(
    ttl = 15,
    authorizedUUID = "my-authorized-uuid",
    channels = listOf(ChannelGrant.name(name = "my-channel", read = true))
).async { result ->
        result.onFailure {
            // Handle error
        }.onSuccess {
            // Handle result
            // Provide logic to return this token to your client
        }
    }
```

#### Go

```go
pnconfig := pubnub.NewConfig()
  pnconfig.SubscribeKey = "MySubscribeKey"
  pnconfig.PublishKey = "MyPublishKey"
  pnconfig.SecretKey = "MySecretKey"
  pnconfig.SetUserId(UserId("myUniqueUserId"))
  pn := pubnub.NewPubNub(pnconfig)

pn := pubnub.NewPubNub(pnconfig)
res, status, err := pn.GrantToken().
        TTL(15).
        AuthorizedUUID("my-authorized-uuid").
        Channels(map[string]pubnub.ChannelPermissions{
                "my_channel": {
                        Read: true,
                },
        }).
        Execute()

// Provide logic to return this token to your client
```

#### C#

```csharp
PNConfiguration pnconfig = new PNConfiguration();
pnconfig.SubscribeKey = "mySubscribeKey";
pnconfig.PublishKey = "myPublishKey";
pnconfig.SecretKey = "mySecretKey";
pnconfig.UserId = "MyUniqueUserId";
Pubnub pubnub = new Pubnub(pnconfig);

PNResult<PNAccessManagerTokenResult> grantTokenResponse = await pubnub.GrantToken()
  .TTL(15)
  .AuthorizedUuid("my-authorized-uuid")
  .Resources(new PNTokenResources()
  {
    Channels = new Dictionary<string, PNTokenAuthValues>() {
                { "my-channel", new PNTokenAuthValues() { Read = true } } } // False to disallow
  })
  .ExecuteAsync();
PNAccessManagerTokenResult grantTokenResult = grantTokenResponse.Result;
PNStatus grantTokenStatus = grantTokenResponse.Status;
// PNAccessManagerTokenResult is a parsed and abstracted response from the server
if (!grantTokenStatus.Error && grantTokenResult != null)
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(grantTokenResult));
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(grantTokenStatus));
}

// Provide logic to return this token to your client
```

#### Dart

```dart
final myKeyset = Keyset(
  subscribeKey: 'mySubscribeKey',
  publishKey: 'myPublishKey',
  secretKey: 'mySecretKey',
  userId: UserId('yourUniqueUserId')
  );

  // Prepare the request object
  var request = pubnub.requestToken(ttl: 15, authorizedUUID: 'my-authorized-uuid');
  request.add(ResourceType.channel, name: 'my-channel', read: true);

  // Send the token request
  var token = await pubnub.grantToken(request);
  print('grant token = $token');

  // Provide logic to return this token to your client
```

#### PHP

```php
use PubNub\PNConfiguration;
$pnConfiguration = new PNConfiguration();
$pnConfiguration->setSubscribeKey("MySubscribeKey");
$pnConfiguration->setPublishKey("MyPublishKey");
$pnConfiguration->setSecretKey("MySecretKey");
$pnConfiguration->setUserId("MyUniqueUserId");

$token = $pubnub->grantToken()
    ->ttl(15)
    ->authorizedUuid('my-authorized-uuid')
    ->addChannelResources([
        'my-channel' => ['read' => true, 'write' => true, 'update' => true],
    ])
    ->sync();
);

// Provide logic to return this token to your client
```

#### Ruby

```ruby
pubnub = Pubnub.new(
    subscribe_key: 'my_subscribe_key',
    publish_key: 'my_publish_key',
    secret_key: 'my_secret_key',
    user_id: 'myUniqueUserId'
);

pubnub.grant_token(
    ttl: 15,
    authorized_uuid: "my-authorized-uuid",
    channels: {
      "my-channel": Pubnub::Permissions.res(
        read: true
      )
    }
);

# Provide logic to return this token to your client
```

## Billing and pricing

Transactions in Access Manager v3 are counted in the same way as in v2, but you need to consider these changes and their effects:

1. In Access Manager v2, a developer has to make separate grant calls for the same client device. This means that you have to make separate grant calls if you want to grant different permissions for multiple resources (read to channel1 and write to channel2). In Access Manager v3, you can create multiple resource-permission mappings in a single call. This change can lower your monthly bill as you may observe some reduction in the overall number of transactions.
2. In Access Manager v3, using the default ttl of 1440 is best for most use cases. If your ttl in v2 was set for a longer period of time, migrating to Access Manager v3 would incur more grant calls and may impact your monthly bill.
3. In Access Manager v2, the authKey is a permissions identifier. These identifiable permissions are stored and replicated in PubNub's databases. In Access Manager v3, revoked tokens are stored on a denylist in the PubNub database. Typically, the storage space used by the v2 permissions is much bigger compared to the storage space used by the v3's denylist. However, database entries of both the v2 and v3 impact your storage capacity and are charged towards your monthly bill.

:::tip Access Manager v2
If you want to learn more about the previous version (v2) of Access Manager, refer to this [feature description](https://www.pubnub.com/docs/general/security/access-control-v2).
:::

## Terms in this document

* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
