Re-sign JWT tokens for safer server deployments
This commit is contained in:
parent
d6860160f4
commit
fb11295168
13
README.md
13
README.md
|
@ -25,7 +25,11 @@ It is very much in the style of the old-school Flash based webcam chat rooms of
|
||||||
* Specify multiple Public Channels that all users have access to.
|
* Specify multiple Public Channels that all users have access to.
|
||||||
* Users can open direct message (one-on-one) conversations with each other.
|
* Users can open direct message (one-on-one) conversations with each other.
|
||||||
* No long-term server side state: messages are pushed out as they come in.
|
* No long-term server side state: messages are pushed out as they come in.
|
||||||
|
* Users may share pictures and GIFs from their computer, which are pushed out as `data:` URLs (images scaled and metadata stripped by server) directly to connected chatters with no storage required.
|
||||||
* Users may broadcast their webcam which shows a camera icon by their name in the Who List. Users may click on those icons to open multiple camera feeds of other users they are interested in.
|
* Users may broadcast their webcam which shows a camera icon by their name in the Who List. Users may click on those icons to open multiple camera feeds of other users they are interested in.
|
||||||
|
* Mutual webcam options: users may opt that anyone who views their cam must also be sharing their own camera first.
|
||||||
|
* Users may mark their own cameras as explicit/NSFW which marks the icon in red so other users can get a warning before clicking in (if NSFW is enabled in the settings.toml)
|
||||||
|
* Users may boot people off their camera, and to the booted person it appears the same as if the broadcaster had turned their camera off completely - the chat server lies about the camera status so the booted user can't easily tell they'd been booted.
|
||||||
* Mobile friendly: works best on iPads and above but adapts to smaller screens well.
|
* Mobile friendly: works best on iPads and above but adapts to smaller screens well.
|
||||||
* WebRTC means peer-to-peer video streaming so cheap on hosting costs!
|
* WebRTC means peer-to-peer video streaming so cheap on hosting costs!
|
||||||
* Simple integration with your existing userbase via signed JWT tokens.
|
* Simple integration with your existing userbase via signed JWT tokens.
|
||||||
|
@ -44,12 +48,16 @@ Some important features still lacking:
|
||||||
On first run it will create the default settings.toml file for you which you may then customize to your liking:
|
On first run it will create the default settings.toml file for you which you may then customize to your liking:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
Version = 2
|
||||||
Title = "BareRTC"
|
Title = "BareRTC"
|
||||||
Branding = "BareRTC"
|
Branding = "BareRTC"
|
||||||
WebsiteURL = "https://www.example.com"
|
WebsiteURL = "https://www.example.com"
|
||||||
UseXForwardedFor = true
|
|
||||||
CORSHosts = ["https://www.example.com"]
|
CORSHosts = ["https://www.example.com"]
|
||||||
PermitNSFW = true
|
PermitNSFW = true
|
||||||
|
UseXForwardedFor = true
|
||||||
|
WebSocketReadLimit = 41943040
|
||||||
|
MaxImageWidth = 1280
|
||||||
|
PreviewImageWidth = 360
|
||||||
|
|
||||||
[JWT]
|
[JWT]
|
||||||
Enabled = false
|
Enabled = false
|
||||||
|
@ -79,6 +87,9 @@ A description of the config directives includes:
|
||||||
* **UseXForwardedFor**: set it to true and (for logging) the user's remote IP will use the X-Real-IP header or the first address in X-Forwarded-For. Set this if you run the app behind a proxy like nginx if you want IPs not to be all localhost.
|
* **UseXForwardedFor**: set it to true and (for logging) the user's remote IP will use the X-Real-IP header or the first address in X-Forwarded-For. Set this if you run the app behind a proxy like nginx if you want IPs not to be all localhost.
|
||||||
* **CORSHosts**: your website's domain names that will be allowed to access [JSON APIs](#JSON APIs), like `/api/statistics`.
|
* **CORSHosts**: your website's domain names that will be allowed to access [JSON APIs](#JSON APIs), like `/api/statistics`.
|
||||||
* **PermitNSFW**: for user webcam streams, expressly permit "NSFW" content if the user opts in to mark their feed as such. Setting this will enable pop-up modals regarding NSFW video and give broadcasters an opt-in button, which will warn other users before they click in to watch.
|
* **PermitNSFW**: for user webcam streams, expressly permit "NSFW" content if the user opts in to mark their feed as such. Setting this will enable pop-up modals regarding NSFW video and give broadcasters an opt-in button, which will warn other users before they click in to watch.
|
||||||
|
* **WebSocketReadLimit**: sets a size limit for WebSocket messages - it essentially also caps the max upload size for shared images (add a buffer as images will be base64 encoded on upload).
|
||||||
|
* **MaxImageWidth**: for pictures shared in chat the server will resize them down to no larger than this width for the full size view.
|
||||||
|
* **PreviewImageWidth**: to not flood the chat, the image in chat is this wide and users can click it to see the MaxImageWidth in a lightbox modal.
|
||||||
* **JWT**: settings for JWT [Authentication](#authentication).
|
* **JWT**: settings for JWT [Authentication](#authentication).
|
||||||
* Enabled (bool): activate the JWT token authentication feature.
|
* Enabled (bool): activate the JWT token authentication feature.
|
||||||
* Strict (bool): if true, **only** valid signed JWT tokens may log in. If false, users with no/invalid token can enter their own username without authentication.
|
* Strict (bool): if true, **only** valid signed JWT tokens may log in. If false, users with no/invalid token can enter their own username without authentication.
|
||||||
|
|
|
@ -2,4 +2,4 @@ package barertc
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const PingInterval = 15 * time.Second
|
const PingInterval = 30 * time.Second
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.kirsle.net/apps/barertc/pkg/config"
|
"git.kirsle.net/apps/barertc/pkg/config"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
@ -53,3 +54,18 @@ func ParseAndValidate(tokenStr string) (*Claims, bool, error) {
|
||||||
|
|
||||||
return claims, authOK, nil
|
return claims, authOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSign will sign a new JWT token for existing claims. The chat server does this to send refreshed tokens
|
||||||
|
// to the front-end so the server can reboot gracefully, clients reconnect and not be told their auth had
|
||||||
|
// expired. New token expires after 5 minutes.
|
||||||
|
func (c Claims) ReSign() (string, error) {
|
||||||
|
// Refresh timestamps.
|
||||||
|
c.ExpiresAt = jwt.NewNumericDate(time.Now().Add(5 * time.Minute))
|
||||||
|
c.IssuedAt = jwt.NewNumericDate(time.Now())
|
||||||
|
c.NotBefore = jwt.NewNumericDate(time.Now())
|
||||||
|
|
||||||
|
// Generate the signed token and return it.
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
||||||
|
ss, err := token.SignedString([]byte(config.Current.JWT.SecretKey))
|
||||||
|
return ss, err
|
||||||
|
}
|
||||||
|
|
|
@ -188,8 +188,19 @@ func (s *Server) WebSocket() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case <-pinger.C:
|
case <-pinger.C:
|
||||||
|
// Send a ping, and a refreshed JWT token if the user sent one.
|
||||||
|
var token string
|
||||||
|
if sub.JWTClaims != nil {
|
||||||
|
if jwt, err := sub.JWTClaims.ReSign(); err != nil {
|
||||||
|
log.Error("ReSign JWT token for %s: %s", sub.Username, err)
|
||||||
|
} else {
|
||||||
|
token = jwt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub.SendJSON(Message{
|
sub.SendJSON(Message{
|
||||||
Action: ActionPing,
|
Action: ActionPing,
|
||||||
|
JWTToken: token,
|
||||||
})
|
})
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
pinger.Stop()
|
pinger.Stop()
|
||||||
|
|
|
@ -608,6 +608,15 @@ const app = Vue.createApp({
|
||||||
this.disconnect = true;
|
this.disconnect = true;
|
||||||
break;
|
break;
|
||||||
case "ping":
|
case "ping":
|
||||||
|
// New JWT token?
|
||||||
|
if (msg.jwt) {
|
||||||
|
this.jwt.token = msg.jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset disconnect retry counter: if we were on long enough to get
|
||||||
|
// a ping, we're well connected and can reconnect no matter how many
|
||||||
|
// times the chat server is rebooted.
|
||||||
|
this.disconnectCount = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unexpected action: %s", JSON.stringify(msg));
|
console.error("Unexpected action: %s", JSON.stringify(msg));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user