You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I spoke to an OAuth guru to check where we're at and get some guidance, as my OAuth2 knowledge is a bit fuzzy. Our login mechanism has a few oddities that would be nice to sort out, but it's by no means a priority.
tldr What we have now currently works, isn't a security risk, and doesn't cause any major problems. However, we could tidy this up in one of two ways if we wanted to tidy things up a bit. The way proposed below is only one option, but it changes little and entirely removes the team-finder.auth table in the DB, at the expense of the discord bot being given more work to do.
Current process/data flow
User triggers login (clicks Login button, tries to view authenticated-only page, etc)
User is redirected to discord.com using the authorisation code flow
User logs in, is redirected from discord back to API
We store these tokens in the DB alongside a unique securely random id token
User is redirected back to UI with a JWT. This JWT only contains id as a meaningful piece of data
On every authorised action afterwards, the JWT is sent in an Authorization: Bearer xxxxxxx header
We use this header to make a /userinfo call to discord to retrieve the user ID and their name
This flow worked sterlingly at launch, but over time is now slightly disjointed for our needs. For context, at the original time of development:
we didn't have the bot integration we do now (which can do most of the work we use /userinfo for)
Ktor didn't natively support a Bearer token (or any other simple API auth mechanism that worked across domains)
we needed a cross-domain compliant approach
(IIRC) we also needed it pretty quickly
Proposal - user and guild IDs in the JWT, stop storing all tokens
We could fully put more user data into the JWT and use this as the user data store without hitting discord per request. This would save time hitting discord, although some changes to the user state will only update on logout/login (such as server nickname).
In order to get this data (e.g. server nickname, so we can display a "Hi $name" message on login) we could use the Javacord bot integration we now have. The only piece of data we get from /userinfo that we need is the unique discord user ID. That ID (plus the guild it is attached to) is enough for the Javacord bot to get the guild status, server nickname, DM permissions etc. status that we rely on.
We could instead just track the user ID and guild ID in the JWT (plus perhaps any info used by the UI, like nickname), bin off our external /userinfo endpoint (as we would pull this data once by hitting discord and saving in the JWT), and let the bot do the work of wrangling discord calls.
The end login flow would look something like this:
User triggers login (clicks Login button, tries to view authenticated-only page, etc)
User is redirected to discord.com using the authorisation code flow
User logs in, is redirected from discord back to API
Using the accessToken only, we hit the discord API to retrieve the discord ID for this user
User is redirected back to UI with a JWT. This new JWT contains the discord ID and guild ID of the current login session
On every authorised action afterwards, the JWT is sent in an Authorization: Bearer xxxxxxx header
We unpack this header and use the IDs to perform authenticated actions (no repeated hitting of discord required)
JWT schema
The proposed JWT would look something like this:
{
"exp": <int: timestamp of token expiry>,"iat": <int: timestamp of token issue>,"iss": "findyourjam.team",
"sub": <string: ID of the jam; e.g. "gmtk">,"discordId": <string: ID of user in discord>,"guildId": <string, ID of guild user has logged into in discord>
}
The guildId is not a required field here, but would have a database lookup on requests that need to translate from a local jam ID gmtk into the guild ID. This won't be a blocker yet but will be relevant if/when this project handles multiple jams.
Related tasks
Currently the signing secret for all JWTs is secret (in the application.conf file). This isn't a security problem because the wrapped value is a secure random string; if we move to using plaintext values in the JWT, the JWT config values need updating and jwt.secret needs to not live in code!
Although this proposal removes /userinfo as a mechanism to query discord, we do need a way to validate the sent JWT. The JWT needs to be signed correctly and includes an expiry field which we use as the TTL for a login session, both of which need using; in order to improve the UX for a user who's session has expired, we could (but I don't think it's a requirement) to have a /validate endpoint which we call asynchronously from the UI. I'm a little hazy on the OAuth2 spec, but my gut reaction is this would let us gracefully expire a login session without waiting for the user to try and perform an authenticated action.
The text was updated successfully, but these errors were encountered:
I spoke to an OAuth guru to check where we're at and get some guidance, as my OAuth2 knowledge is a bit fuzzy. Our login mechanism has a few oddities that would be nice to sort out, but it's by no means a priority.
tldr What we have now currently works, isn't a security risk, and doesn't cause any major problems. However, we could tidy this up in one of two ways if we wanted to tidy things up a bit. The way proposed below is only one option, but it changes little and entirely removes the
team-finder.auth
table in the DB, at the expense of the discord bot being given more work to do.Current process/data flow
id
tokenid
as a meaningful piece of dataAuthorization: Bearer xxxxxxx
header/userinfo
call to discord to retrieve the user ID and their nameThis flow worked sterlingly at launch, but over time is now slightly disjointed for our needs. For context, at the original time of development:
/userinfo
for)Proposal - user and guild IDs in the JWT, stop storing all tokens
We could fully put more user data into the JWT and use this as the user data store without hitting discord per request. This would save time hitting discord, although some changes to the user state will only update on logout/login (such as server nickname).
In order to get this data (e.g. server nickname, so we can display a "Hi $name" message on login) we could use the Javacord bot integration we now have. The only piece of data we get from
/userinfo
that we need is the unique discord user ID. That ID (plus the guild it is attached to) is enough for the Javacord bot to get the guild status, server nickname, DM permissions etc. status that we rely on.We could instead just track the user ID and guild ID in the JWT (plus perhaps any info used by the UI, like nickname), bin off our external
/userinfo
endpoint (as we would pull this data once by hitting discord and saving in the JWT), and let the bot do the work of wrangling discord calls.The end login flow would look something like this:
accessToken
only, we hit the discord API to retrieve the discord ID for this userAuthorization: Bearer xxxxxxx
headerJWT schema
The proposed JWT would look something like this:
The
guildId
is not a required field here, but would have a database lookup on requests that need to translate from a local jam IDgmtk
into the guild ID. This won't be a blocker yet but will be relevant if/when this project handles multiple jams.Related tasks
Currently the signing secret for all JWTs is
secret
(in the application.conf file). This isn't a security problem because the wrapped value is a secure random string; if we move to using plaintext values in the JWT, the JWT config values need updating andjwt.secret
needs to not live in code!Although this proposal removes
/userinfo
as a mechanism to query discord, we do need a way to validate the sent JWT. The JWT needs to be signed correctly and includes an expiry field which we use as the TTL for a login session, both of which need using; in order to improve the UX for a user who's session has expired, we could (but I don't think it's a requirement) to have a/validate
endpoint which we call asynchronously from the UI. I'm a little hazy on the OAuth2 spec, but my gut reaction is this would let us gracefully expire a login session without waiting for the user to try and perform an authenticated action.The text was updated successfully, but these errors were encountered: