Skip to content

Heartbeat API Failing with 403 Error Post PubNub Access Manager Migration to v3 – Invalid Token Signature #431

@vinaybongale

Description

@vinaybongale

I am in the process of migrating from PubNub Access Manager v2 to v3 and encountering multiple issues related to token generation and validation. Below are the steps and changes I've made so far, along with the observed issues:


Changes Implemented:

Server-Side (Ruby):

  • Updated the token generation logic to use grant_token instead of grant.
  • Removed the auth_key as it is no longer required.
  • Here is the updated server-side token generation code:
def self.generate_token(user, entity_id)
  pubnub = Pubnub.new(next? ? private_config(user.uuid) : private_config)
  details = presence_details user
  permissions = {
    ttl: PRESENCE_TTL, # Token Time-to-Live in minutes (24 hours)
    authorized_uuid: user.uuid,
    channels: {
      "#{entity_id}#{PUBNUB_CHANNEL_DELIMETER}#{PUBNUB_WILDCARD}" => Pubnub::Permissions.res(
        read: true,
        write: true,
        manage: false
      )
    }
  }

  begin
    token_future = pubnub.grant_token(permissions)
    token_response = token_future.value
    if token_response.is_a?(Pubnub::Envelope) && token_response.status[:code] == 200
      token = token_response.result[:data]["token"]
      details.except(:auth_key).merge(token: token, user_id: user.uuid)
    else
      Rails.logger.info "Failed to generate token: #{token_response.status[:error]}"
    end
  rescue => e
    Rails.logger.info "Error: #{e.message}"
  end
end

Frontend Client (JavaScript/TypeScript):

  • Updated the frontend to use the newly provided token from the server.
  • Relevant client-side connection code:
async connect(options: any = {}) {
  const credentials = await this.options.resolveCredentials();

  if (!this.pubnub) {
    const config = this.options.resolveConfig(credentials, options);
    const { uuid: userId, publishKey, subscribeKey } = config;
    const v3PubNubConfig = { userId, publishKey, subscribeKey };
    this.pubnub = this.options.PubNubConstructor(v3PubNubConfig);
    this.pubnub.setToken(credentials.get('token', null));

    this.emitter = pubnubToEmitter(this.pubnub);

    this.pubnubPromise = promiseForEmitter(this.emitter);
    this.statusPromise = this.pubnubPromise(PubNubEvent.Status);
  } else {
    this.pubnub.setToken(credentials.get('token', null));
  }

  return credentials;
}
  • Additional Context:
    The connect method is a public method of the PubNubTransport class. Below is the constructor for the class where PubNubConstructor is defined which is used to above connection code to initialize PubNub:
export class PubNubTransport {
  constructor(opts: IPubNubTransportOptions) {
    this.options = fp.defaults(
      {
        ClientState: DefaultClientState,
        PubNubConstructor: (config) => new PubNub(config)
      },
      opts
    );

    this.listeners = Immutable.Map<string, Immutable.List<ListenerParameters>>();
  }
}

Observed Issues:

1. Heartbeat API Failure:

  • The Heartbeat API call fails with a 403 Forbidden error, as shown below:
{
  "error": true,
  "status": 403,
  "service": "Access Manager",
  "message": "Forbidden",
  "payload": {
    "channels": [
      "2136.proposal.UHJvcG9zYWw6WzE2NjcxNTksMTU1NDY3OV0=-pnpres",
      "2136.proposal.UHJvcG9zYWw6WzE2NjcxNTksMTU1NDY3OV0="
    ],
    "channel_groups": [],
    "users": [],
    "spaces": [],
    "uuids": []
  }
}
  • Heartbeat API call details:
http://ps19.pndsn.com/v2/subscribe/sub-c-xxxxxx/2136.proposal.UHJvcG9zYWw6WzE2NjcxNTksMTU1NDY3OV0%3D%2C2136.proposal.UHJvcG9zYWw6WzE2NjcxNTksMTU1NDY3OV0%3D-pnpres/0?heartbeat=300&uuid=f5f4bc78-0988-4375-887e-92883f97b1cd&pnsdk=PubNub-JS-Web%2F7.6.3&auth=qEF2AkF0GmeIDcFDdHRsGQWgQ3Jlc6VEY2hhbqFmMjEzNi4qA0NncnCgQ3NwY6BDdXNyoER1dWlkoENwYXSlRGNoYW6gQ2dycKBDc3BjoEN1c3KgRHV1aWSgRG1ldGGgRHV1aWR4JGY1ZjRiYzc4LTA5ODgtNDM3NS04ODdlLTkyODgzZjk3YjFjZENzaWdYIDWEOHvZrcTpiRX86DTc-yDuGL3HMgLgBuZnAoX1swZx
  • Despite migrating to v3, the v2 version of the subscribe API is being called.

2. Invalid Token Signature:

  • The token generated and returned by the backend fails signature validation on [jwt.io](https://jwt.io).
  • Below is an example token that produces the "Invalid Signature" error:
qEF2AkF0GmeGO7JDdHRsGQWgQ3Jlc6VEY2hhbqFmMjEzNi4qA0NncnCgQ3NwY6BDdXNyoER1dWlkoENwYXSlRGNoYW6gQ2dycKBDc3BjoEN1c3KgRHV1aWSgRG1ldGGgRHV1aWR4JGY1ZjRiYzc4LTA5ODgtNDM3NS04ODdlLTkyODgzZjk3YjFjZENzaWdYIDWEOHvZrcTpiRX86DTc-yDuGL3HMgLgBuZnAoX1swZx
Screenshot 2025-01-16 at 1 19 13 AM

Questions:

  1. Is there any specific configuration or step I might have missed during the migration from v2 to v3?
  2. Why is the Heartbeat API still calling the v2 endpoint even after migrating to v3 and updating the token management logic?
  3. Does the token generated in the grant_token API need any specific permissions for heartbeat to work correctly?
  4. Why does the generated token fail signature validation on jwt.io? Are there specific signing requirements that need to be handled differently for v3?

Thank you for your guidance! Let me know if you need more details.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions