ircv3-ideas
ircv3-ideas copied to clipboard
BUFFERTOKEN to provide JWT tokens for external services
Networks often provide services alongside the network such as wikis, pastebins, polls, etc. In the cases where a service is tied to a channel there could be a function for the IRCd to provide a JWT token that the service can verify the user with, without needing to resort to more complicated bots/xmlrpc/etc.
In my test case I'm using the JWT token in a web client to send to a audio/video call service which verifies then uses the modes from the token to apply permissions and grant access. Currently this works great and is very simple to make use of.
Example:
C -> S BUFFERTOKEN #testchannel
S -> C BUFFERTOKEN #testchannel eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGFubmVsIjoiI3Rlc3RjaGFubmVsIiwiZXhwIjoxNTI5OTE3NTEzLCJqb2luZWQiOnRydWUsIm5pY2siOiJ0ZXN0bmljayIsInNlcnZlciI6ImlyYy5leGFtcGxlLm9yZyIsInRpbWVfam9pbmVkIjoxNTI5OTE3NTAxLCJtb2RlcyI6WyJvIl19.WjiZOggLDmF80TbkCcSY5pXnYsnKVj8qnvnpBpDrVZ0
Where that token includes the payload:
{
"channel": "#testchannel",
"exp": 1529917513,
"joined": true,
"nick": "testnick",
"server": "irc.example.org",
"time_joined": 1529917501,
"modes": ["o"]
}
We could also pass * instead of a buffer name to get a token relevant to the connection in general. Could this be improved or to include other bits of data in the payload that would benefit external services?
Sounds interesting. Can you provide a more complete explanation of what this is used for and how it works? Is the a/v service integrated with the ircd or a services package? What does the client do with the JWT after it receives it. What are the various Json fields used for?
The idea is that external services are not part of the IRCd at all. No shared databases, no interaction between them needed at all. An external service may be any web application that an IRC network wants to offer to its users. This,
- Makes it easier to deploy an external service as it doesn't need access to your IRCd
- Improves security in that a misconfigured or vulnerable external service cannot impact the IRC network or user accounts
- Provides a common method to verify user status on a channel (connected, chan modes) while using well known methods that are available for many languages and projects out there.
Examples
In case of an audio/video conference calling example, we can take the Jitsi Meet conference web service as an example. This service has built in JWT verification in that an application can send a user to a URL that contains a JWT token, and if the Jitsi Meet server verifies this token successfully, the user is granted access to that conference room.
When an IRC client wants to join a conference room, it would first call BUFFERTOKEN #testchannel to receive a JWT token from the IRCd. The client would then open a browser window navigating to the Jiti Meet URL while passing that token. It is up to the client to decide how and where to use this token, eg. via a "Jitsi Call" button for example.
Other example could be a pastebin that only accepts pastes from a specific channel or only from users actively connected to the network, or an etherpad for a specific channel. The client would make the BUFFERTOKEN request, and open the web application URL with the JWT token in its URL.
Where it would / could be used
I would imagine this would mostly be used by web clients where the web client admins add these external services to their websites. This does not stop non-web clients from providing services, however since each network and external service needs a "secret" key to sign and verify the data, this would prove difficult to make generally available to multi-network clients.
The data
The JWT token structure can be found here, along with it's most common claims (fields) https://jwt.io/introduction/
In case of the example given at the top of this thread:
{
"channel": "#testchannel", -- The channel name this token was generated for by the IRCd
"exp": 1529917513, -- Standard JWT claim, expiry time for this token
"joined": true, -- If the user that generated this token is actually joined to the channel
"nick": "testnick", -- The nick of the user that generated this token
"server": "irc.example.org", -- The server name that generated this token
"time_joined": 1529917501, -- The time in which the user joined the channel
"modes": ["o"] -- The channel modes the user has in this channel
}
A web service receiving this token will verify it using its JWT library which ensures the data has not been tampered with (the IRCd and the service requires a shared secret key). The service will then know if the user is within the channel it claims to be, how long the user has been in the channel, which nick the user has, and what modes the user has. The service may only provide functions for operators of a channel for example.
As long as there is a standard minimum set of claims that external services can expect, any other interesting data that an external service may use can be included here.
Taking it further
The above example is a live example that works nicely so far. However it could be taken further by including these claims:
account-- If the user is logged in, a non-empty string with the users account namenet_modes-- If the IRCd deems any user modes are interesting, include them here in the same format asmodes. Eg. +o.
Input from others to decide on what data should be included at minimum would be needed here.
Could this be done with metadata instead of introducing a new verb?
Also applies to metadata-based solutions: the line length issue is going to hurt at some point, JWT claims can get big.
I like the idea of this, interesting to see what use cases it could enable.
Ehh, I'm not too sure this would fit under metadata. It's not gonna remain static over time (the exp is presumably gonna be updated every time BUFFERTOKEN is called, etc). I do like the idea, but think it'd be worth looking at an explicit continuation syntax for it.
E.g. for server replies, BUFFERTOKEN <target> * <data> indicating that there's another reply coming, and then just BUFFERTOKEN <target> <data> for the final one. Or instead, go with the good ole' SASL approach of if <data> is 400 characters long, wait for another reply (even just an empty one signifying the end of the JWT).
Not purely a bikeshed, but: Is there a reason you chose the verb BUFFERTOKEN instead of JWT? I mostly get the rationale and use case, just wondering if there's something about that particular name that helps clarify your motivation more.
@jwheare just for descriptiveness. JWT on its own could mean a JWT token for anything. It could also just be TOKEN since it could return claims just about the user on a network (connected time, nick, account, server address) rather than specifically on a buffer.
@DanielOaks @dequis good points on the line length, it's possible at a push. I'm all for the * indicator of more data coming so its consistent with CAP LS
I'd prefer something that describes the data format a bit better than a generic term like TOKEN which could also mean anything. Since as you said, it's not just buffers, JWT seems like it would make more sense, as a more descriptive verb.
Should server be iss instead? And time_joined → nbf?
Re amount of data: many of these fields are needed or not depending on the purpose why client requests the token.
however since each network and external service needs a "secret" key to sign and verify the data, this would prove difficult to make generally available to multi-network clients.
External service doesn't need to know the secret, only the public key. I see another problem with non-network-runned-clients though: how should client discover what kind of services the token can be useful for? When/why should it request it?
One more thing: need an error message for "you're not on channel, no token for you"
Ah, for not being joined to the channel, the token would just include "joined": false instead of true.
how should client discover what kind of services the token can be useful for? When/why should it request it?
Yeah that's an interesting q. Gonna depend on the specific service involved it feels like, since the client'll interact with them differently. Interested to see what we can build up on that end, alongside just the token command.
server being iss would make sense, good call. Can't say I've never seen nbf being used anywhere, but looking it up it makes more sense to see iat (issued at) if it's to include any of these?
@DarthGandalf depending on the JWT token generator, it can either use RSA and alike with pub/private keys or HMAC shared key which is the most common format. I can't see the need to against the norm here to simply provide proof that somebody is on a channel to other self hosted services.
As for discovering what services are available, that would be entirely down to the client to provide them as I see it. If I set up a web client for my network that most users use, I could configure a one click button that retrieves a token from the IRCd and auto open a new window for a jitsi service. Or some other calling services that takes a JWT token. I can't feasibly see a situation where an IRCd would imagine every use that a client may have.
Before I start writing something up properly, I just want to go over the available claims that should be provided at a minimum.
Here are the claims that I think will be most useful, allowing external services to provide functionality based on:
- Users connected to a network and/or specific server
- Guests or logged into an account
- Users with a specific user mode (eg. opers only)
- Users on a specific channel
- Users with a specific mode on a channel (eg. chan ops only)
Common claims
These claims are included in response to any EXTJWT command.
exp1529917513Expiry time for this token. Usually < 1min from token generationiss"irc.example.org"The server name that generated this tokennick"testnick"The nick of the user that generated this tokenaccount"testnick"The account name of the user that generated this token. Empty if not logged innet_modes["o"]User modes the IRCd wishes to disclose. Eg, is the user an oper
Extra claims when in context of a channel
When a channel name is provided to the EXTJWT command (EXTJWT #channel), these claims will also be included.
channel"#channel"The channel name this token is related tojoinedtrueIf the user that generated this token is actually joined to the channeltime_joined1529917501, -- The time in which the user joined the channel.modes["o"]The channel modes the user has in this channel
Does anyone have any thoughts for other claims that may be useful to an external service?