Allow custom JWT service expiry times

This commit is contained in:
Daniel Oaks 2020-04-15 20:09:51 +10:00 committed by Shivaram Lingamneni
parent 0bbb5d121d
commit 9b998a7582
4 changed files with 36 additions and 14 deletions

View File

@ -164,9 +164,13 @@ server:
# these services can integrate with the ircd using JSON Web Tokens (https://jwt.io) # these services can integrate with the ircd using JSON Web Tokens (https://jwt.io)
# sometimes referred to with 'EXTJWT' # sometimes referred to with 'EXTJWT'
jwt-services: jwt-services:
# # service name -> secret string the service uses to verify our tokens # # service name
# call-host: call-hosting-secret-token # call-host:
# image-host: image-hosting-secret-token # # custom expiry length, default is 30s
# expiry-in-seconds: 45
# # secret string to verify the generated tokens
# secret: call-hosting-secret-token
# allow use of the RESUME extension over plaintext connections: # allow use of the RESUME extension over plaintext connections:
# do not enable this unless the ircd is only accessible over internal networks # do not enable this unless the ircd is only accessible over internal networks

View File

@ -190,9 +190,13 @@ server:
# these services can integrate with the ircd using JSON Web Tokens (https://jwt.io) # these services can integrate with the ircd using JSON Web Tokens (https://jwt.io)
# sometimes referred to with 'EXTJWT' # sometimes referred to with 'EXTJWT'
jwt-services: jwt-services:
# # service name -> secret string the service uses to verify our tokens # # service name
# call-host: call-hosting-secret-token # call-host:
# image-host: image-hosting-secret-token # # custom expiry length, default is 30s
# expiry-in-seconds: 45
# # secret string to verify the generated tokens
# secret: call-hosting-secret-token
# allow use of the RESUME extension over plaintext connections: # allow use of the RESUME extension over plaintext connections:
# do not enable this unless the ircd is only accessible over internal networks # do not enable this unless the ircd is only accessible over internal networks

View File

@ -471,6 +471,11 @@ type TorListenersConfig struct {
MaxConnectionsPerDuration int `yaml:"max-connections-per-duration"` MaxConnectionsPerDuration int `yaml:"max-connections-per-duration"`
} }
type JwtServiceConfig struct {
ExpiryInSeconds int64 `yaml:"expiry-in-seconds"`
Secret string
}
// Config defines the overall configuration. // Config defines the overall configuration.
type Config struct { type Config struct {
Network struct { Network struct {
@ -502,9 +507,9 @@ type Config struct {
MOTDFormatting bool `yaml:"motd-formatting"` MOTDFormatting bool `yaml:"motd-formatting"`
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
proxyAllowedFromNets []net.IPNet proxyAllowedFromNets []net.IPNet
WebIRC []webircConfig `yaml:"webirc"` WebIRC []webircConfig `yaml:"webirc"`
JwtServices map[string]string `yaml:"jwt-services"` JwtServices map[string]JwtServiceConfig `yaml:"jwt-services"`
MaxSendQString string `yaml:"max-sendq"` MaxSendQString string `yaml:"max-sendq"`
MaxSendQBytes int MaxSendQBytes int
AllowPlaintextResume bool `yaml:"allow-plaintext-resume"` AllowPlaintextResume bool `yaml:"allow-plaintext-resume"`
Compatibility struct { Compatibility struct {
@ -922,6 +927,13 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Server.capValues[caps.Multiline] = multilineCapValue config.Server.capValues[caps.Multiline] = multilineCapValue
} }
// confirm jwt config
for name, info := range config.Server.JwtServices {
if info.Secret == "" {
return nil, fmt.Errorf("Could not parse jwt-services config, %s service has no secret set", name)
}
}
// handle legacy name 'bouncer' for 'multiclient' section: // handle legacy name 'bouncer' for 'multiclient' section:
if config.Accounts.Bouncer != nil { if config.Accounts.Bouncer != nil {
config.Accounts.Multiclient = *config.Accounts.Bouncer config.Accounts.Multiclient = *config.Accounts.Bouncer

View File

@ -922,7 +922,6 @@ func extjwtHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
} }
claims := jwt.MapClaims{ claims := jwt.MapClaims{
"exp": time.Now().Unix() + expireInSeconds,
"iss": server.name, "iss": server.name,
"sub": client.Nick(), "sub": client.Nick(),
"account": accountName, "account": accountName,
@ -945,8 +944,6 @@ func extjwtHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
} }
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// we default to a secret of `*`. if you want a real secret setup a service in the config~ // we default to a secret of `*`. if you want a real secret setup a service in the config~
service := "*" service := "*"
secret := "*" secret := "*"
@ -954,14 +951,19 @@ func extjwtHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
service = strings.ToLower(msg.Params[1]) service = strings.ToLower(msg.Params[1])
c := server.Config() c := server.Config()
var exists bool info, exists := c.Server.JwtServices[service]
secret, exists = c.Server.JwtServices[service]
if !exists { if !exists {
rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_SERVICE", client.t("No such service")) rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_SERVICE", client.t("No such service"))
return false return false
} }
secret = info.Secret
if info.ExpiryInSeconds != 0 {
expireInSeconds = info.ExpiryInSeconds
}
} }
claims["exp"] = time.Now().Unix() + expireInSeconds
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(secret)) tokenString, err := token.SignedString([]byte(secret))
if err == nil { if err == nil {