0

I'm trying to use ably.io with Angular and Azure Functions using the JWT way of authenticating since it's secure, but I'm having issues with configuring the angular side of it. The use case is for a live auction site to update bids in realtime. There isn't a specific angular tutorial for this so I'm trying to piece it together. Also this code

          realtime.connection.once('connected', function () {
            console.log('Client connected to Ably using JWT')
            alert("Client successfully connected Ably using JWT auth")
          });

never throws the alert so I don't think it's working right. I used to have it working where I wasn't using ably JWT, but had the API key on the client-side in a component like this

let api = "<api key>";
let options: Ably.Types.ClientOptions = { key: api };
let client = new Ably.Realtime(options); /* inferred type Ably.Realtime */
let channel = client.channels.get(
  "auctions"
);

and I could subscribe to that channel and update auctions accordingly by their id inside ngOnInit()

    channel.subscribe(message => {
      const auction = this.posts.find(action => {
        return action.id === message.data.auctionId;
      });

      if (auction) {
        auction.currentBid = message.data.lastBid;
      }
    });

but I need to switch this logic for JWT and somehow feed that JWT token into different components as well.

Ably.io JWT tutorial reference

I put the following in my angular login service

  login(email: string, password: string) {


    const authData: AuthDataLogin = { email: email, password: password };
    return this.http
      .post<{
        token: string;
        expiresIn: number;
        userId: string;
      }>(environment.azure_function_url + "/POST-Login", authData)
      .pipe(takeUntil(this.destroy)).subscribe(response => {
        //JWT login token. Not Ably JWT Token
        const token = response.token; 
        this.token = token;




        if (token) {

          console.log('Fetching JWT token from auth server')

          var realtime = new Ably.Realtime({
            authUrl: "http://localhost:7071/api/AblyAuth"
          });
          realtime.connection.once('connected', function () {
            console.log('Client connected to Ably using JWT')
            alert("Client successfully connected Ably using JWT auth")
          });

...
  }

With my azure function already configured, When I login, the browser console outputs

GET wss://realtime.ably.io/?access_token=<token was here>&format=json&heartbeats=true&v=1.1&lib=js-web-1.1.22

SO it returns my token, but

  1. the alert never happens
  2. I'm not sure how to grab that JWT token that's returned to the browser. I was thinking I could store it in localStorage to share between components and clear out localStorage when user logs out, but I need to be able to subscribe to response and assign the token to a variable, but I didn't see in ably javascript tutorial how to get variable assigned to JWT Token response since it's being called with this syntax.

I appreciate any help with this!

       var realtime = new Ably.Realtime({
            authUrl: "http://localhost:7071/api/AblyAuth"
          });

My azure function looks like

const checkAuth = require('../middleware/check-auth');
var jwt = require("jsonwebtoken")
var appId = '<APP ID>'
var keyId = '<key ID>'
var keySecret = '<key secret>'
var ttlSeconds = 60

var jwtPayload =
{
  'x-ably-capability': JSON.stringify({ '*': ['publish', 'subscribe'] })
}
var jwtOptions =
{
  expiresIn: ttlSeconds,
  keyid: `${appId}.${keyId}`
}
console.log("JwtPayload");
console.log(jwtPayload);
console.log("jwtOptions");
console.log(jwtOptions);

module.exports = function (context, req) {
  console.log("INSIDE ABLY AUTH")
  // checkAuth(context, req);
  console.log('Sucessfully connected to the server auth endpoint')
  jwt.sign(jwtPayload, keySecret, jwtOptions, function (err, tokenId) {


    if (err) {
      console.log("ERR")
      console.log(err)
      console.trace()
      return
    }
    context.res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
    context.res.setHeader('Content-Type', 'application/json')
    console.log('Sending signed JWT token back to client')
    console.log(tokenId)

    context.res = {

      status: 200,

      body: JSON.stringify(tokenId),

      headers: {
        "Access-Control-Allow-Credentials": "true",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type, Set-Cookie",
        "Access-Control-Max-Age": "86400",
        "Vary": "Accept-Encoding, Origin",
        "Content-Type": "application/json"
      }

    };

    context.done();
  })
}
user6680
  • 79
  • 6
  • 34
  • 78

1 Answers1

0

I'd recommend if you're wanting to intercept the JWT prior to passing it to Ably (so as to verify the contents, and also use the JWT for other components), you make use of authCallback instead of authUrl. You can use a function instead of a direct URL, within which you can call the endpoint, and do anything you like with the response, prior to passing the JWT back to the Ably constructor. I've made a JavaScript example of using the authCallback for normal Token Authentication, but the same principle applies.

As to why you're not seeing the alert, it looks like you're sending an invalid JWT for what Ably is expecting, and thus you're not successfully connecting to Ably. For example, you're specifying 'expiresIn' rather than 'exp'. For a token to be considered valid, it expected certain elements in a very specific structure, see the documentation. I'd recommend for this sort of situation where you're not certain what's breaking that you make use of verbose logging, which you can enable in the connection constructor as "log": 4.

Tom Camp
  • 66
  • 3