Open eirikb opened 3 years ago
I haven't been able to find a way to disable anything in the service, but it is quite straight forward to generate a token client-side.
The package @azure/web-pubsub
does not seem to work in a browser (at least not when I tried with parcel, too many node specific dependencies), but the code for generating the token is simple, and does not require any external queries.
To play around I copied this code from the npm package, and used the secret to build a token.
This means super secret key and connection string will be public.
You might think this is madness, and it probably is, I'm just not sure why yet - can the service be managed with the key?
Update: Code sample
If anyone is interested, here is the code I made:
import jwt from "jsonwebtoken";
export function tokenUrl(host: string, key: string, hub: string): string {
const clientUrl = `wss://${host}/client/hubs/${hub}`;
const audience = `https://${host}/client/hubs/${hub}`;
const signOptions: jwt.SignOptions = {
audience: audience,
expiresIn: "60m",
algorithm: "HS256",
};
const payload = {
role: ["webpubsub.sendToGroup", "webpubsub.joinLeaveGroup"],
};
const token = jwt.sign(payload, key, signOptions);
return `${clientUrl}?access_token=${token}`;
}
Notes about the code:
sign
only once. Original code does this twice for no apparent reasonasync
, original code is this for no apparent reasonplease don't do this for a publicly accessible web page, doing this means everyone who can view your web page can abuse your service with this connection string.
@vicancy Thank you for your answer. Could you elaborate on that please? What are the full extent of implications when making the connection string public?
In general, when you have the connection string you can use all the features the service provided, for example, connect to the service, call the REST API of the service to broadcast to any connected clients, close any clients, etc. In a word, when you have the connection string, you are the admin of the service data plane.
I'm aware you would essentially be admin, but I tried to do malicious things, and didn't manage to do much (I don't consider broadcast a problem here).
Kicking out clients though, that's a cool one, didn't see that was possible - I only looked at the JS side of it, and it seemed to only work for "your client" and since I couldn't list clients I couldn't get the ID of others anyway.
Will it be possible to connect to PubSub without a token in the future? How about using MSAL to get a token? (A bit bothersome for "guest" or non-user approaches though). Since User is optional when creating a token I really find it hard to understand why this flow is mandatory. You might consider Functions a splendid approach, but not having to need a function would in my honest opinion be a great option.
Just tried setting expiresIn
to a larger value, and 16y
(or 5999d
) works, that might be a viable solution. Just need a postit:
Hello self, remember to create a new token for that app you created 16 years ago
Since User is optional when creating a token I really find it hard to understand why this flow is mandatory.
I think the main responsibility of this workflow is to prevent malicious users. I understand that you'd like some "anonymous" connect feature. The main concern for Web PubSub to allow anonymous connection is that it is a persistent connection. Web PubSub has limitations to concurrent connections, for example, free instances allow 20 concurrent connections, Unit 100 allows 100K concurrent connections. It is comparatively heavier than the "anonymous request to a web app".
If we allow anonymous connect, the issues we need to solve are:
I understand your concern about the dependency on some server-side. Definitely we'd like to improve that too! We actually had several rounds of internal discussions about this too, to simplify the workflow.
There are several proposals, but the design is not yet finalized and is open for discussion:
Would love your feedback and thoughts!
I think the main responsibility of this workflow is to prevent malicious users.
How does code like this actually help to prevent that? I just have to spam that endpoint with random values for id
to get new clients 🤷
I don't have any good solutions in mind.
How about just allowing anonymous connections?
I do like MSAL, is it possible to create guest/anonymous logins there? I haven't done anything like that before.
That would allow you on your side to have more control of origin and type of connection/token.
Tried to google around for "MSAL" and "guest user" but since B2B is essentially guest users it is a bit difficult.
If the middleware becomes optional, how would join/leave messages be distributed?
My understanding is that these are now controlled by some kind of web hook (Event Handlers in Settings).
Could this be distributed automatically to all the clients who are in the same group(s)? Perhaps controlled by a role.
How does code like this actually help to prevent that?
Ah, that one is a simple demo to show some basic idea, a more real-world case would be your application logic to decide if the client has permission to be connected or so. Some advanced auth samples are: https://github.com/Azure/azure-webpubsub/tree/main/samples/javascript/githubchat, or this https://github.com/benc-uk/chatr
For the guest/anonymous login, could you describe your scenarios a little bit more? Is that for a quicker getting started or your scenario wants anonymous connections? Do you want anonymous connections always connected to the service? If for example, anonymous connections use up the quota of concurrent connections, what do you expect, auto-scaling the service?
Ah, that one is a simple demo to show some basic idea, ...
I understand it's a simple example, but the easiest samples to run and understand are probably the most copied ones.
The other sample, chatr, also creates tokens from querystring so it has the same issue. Of course it does, it is basically allowing anonymous users.
For the guest/anonymous login, could you describe your scenarios a little bit more?
What I really want is to not have a middleware. This would in some sense be allowing anonymous users, but providing a user id from the client would be fine.
How about if the service included:
The chatr sample "api" part is in my eyes a good example of what could have been part of the service, and can be viewed as boilerplate code.
What I really want is to not have a middleware
May I ask the reason for not having a middleware? Don't want a long-run server-side? Want one service to handle all application logic instead of having Web PubSub+Web App? Prefer code-free development?
May I ask the reason for not having a middleware?
Optionally code-less for simple setups and kickstarting projects. Code-full for more advanced setups.
Would it also be possible to include userId
automatically when sending messages to groups?
In the samples all messages have to go via server in order to insert correct user.
userId
is already provided to the service, could this not be inserted by the service?
This would be important for a server-code-less solution.
Hi
Is it possible to use only a client (browser)?
All the examples I've seen so far either rely on creating a temporary token in portal, and/or use some middleware e.g., in nodejs.
I might be misunderstanding something, but