Skip to content

Commit

Permalink
Use JWT login instead of passwords if key is set
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed May 21, 2023
1 parent 3e236f9 commit e5822c7
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 32 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ The bot is configured through environment variables
* `BOTBOT_PICKLE_KEY` - Pickle key for encrypting encryption keys.
* `BOTBOT_REGISTER_SECRET` - Registration shared secret for creating new bot
accounts for users. Required unless the Beeper API URL is set.
* `BOTBOT_LOGIN_JWT_KEY` - JWT secret for logging into bot accounts.
If set, the bot will use Synapse's `org.matrix.login.jwt` login type instead
of password login. Only supports the `HS256` algorithm.
* `BOTBOT_BEEPER_API_URL` - Optional Beeper API server URL for registering
users through the Beeper API instead of directly with Synapse.
* `BOTBOT_LOG_LEVEL` - Log level. Defaults to `debug`.
Expand Down
44 changes: 21 additions & 23 deletions adminapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,28 @@ func IsUsernameAvailable(ctx context.Context, username string) (bool, error) {

func Login(ctx context.Context, userID id.UserID, password string) (*mautrix.RespLogin, error) {
loginClient, _ := mautrix.NewClient(cfg.HomeserverURL, "", "")
resp, err := loginClient.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
Identifier: mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: userID.String(),
},
Password: password,
InitialDeviceDisplayName: "botbot",
})
return resp, err
}
identifier := mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: userID.String(),
}
const deviceDisplayName = "botbot"
if cfg.LoginJWTKey != "" {
return loginClient.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypeSynapseJWT,
Identifier: identifier,
Token: createLoginToken(userID),

func LoginJWT(ctx context.Context, userID id.UserID) (*mautrix.RespLogin, error) {
loginClient, _ := mautrix.NewClient(cfg.HomeserverURL, "", "")
resp, err := loginClient.Login(&mautrix.ReqLogin{
Type: "org.matrix.login.jwt",
Identifier: mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: userID.String(),
},
Token: createLoginToken(userID),
InitialDeviceDisplayName: "botbot",
})
return resp, err
InitialDeviceDisplayName: deviceDisplayName,
})
} else {
return loginClient.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
Identifier: identifier,
Password: password,

InitialDeviceDisplayName: deviceDisplayName,
})
}
}

func RegisterUser(ctx context.Context, username string) (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/caarlos0/env/v8 v8.0.0
github.com/mattn/go-sqlite3 v1.14.16
github.com/rs/zerolog v1.29.1
maunium.net/go/mautrix v0.15.3-0.20230520203315-423c1357143c
maunium.net/go/mautrix v0.15.3-0.20230521113032-12c01c702609
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.15.3-0.20230520203315-423c1357143c h1:M0Zk8qT6RcChYr2+9/vZH0+Z3X1wZY9p1UnvrpC1PfI=
maunium.net/go/mautrix v0.15.3-0.20230520203315-423c1357143c/go.mod h1:h4NwfKqE4YxGTLSgn/gawKzXAb2sF4qx8agL6QEFtGg=
maunium.net/go/mautrix v0.15.3-0.20230521113032-12c01c702609 h1:Q+JZ0TU4hK3pgQUlU1koPA1R0PiivLoKjlDXurjGypw=
maunium.net/go/mautrix v0.15.3-0.20230521113032-12c01c702609/go.mod h1:h4NwfKqE4YxGTLSgn/gawKzXAb2sF4qx8agL6QEFtGg=
9 changes: 3 additions & 6 deletions jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@ import (
type jwtPayload struct {
Subject string `json:"sub"`
Issuer string `json:"iss"`
Audience []string `json:"aud"`
IssuedAt jsontime.Unix `json:"iat"`
ExpiresAt jsontime.Unix `json:"exp"`
}

// var encodedJWTHeader = base64.RawStdEncoding.EncodeToString(`{"alg":"HS256","typ":"JWT"}`)
// var encodedJWTHeader = base64.RawStdEncoding.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`))
const encodedJWTHeader = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9`

func createLoginToken(userID id.UserID) string {
payload, err := json.Marshal(&jwtPayload{
Subject: userID.Localpart(),
Issuer: "botbot",
Audience: []string{"synapse"},
IssuedAt: jsontime.UnixNow(),
ExpiresAt: jsontime.U(time.Now().Add(1 * time.Minute)),
})
Expand All @@ -37,16 +35,15 @@ func createLoginToken(userID id.UserID) string {

signer := hmac.New(sha256.New, []byte(cfg.LoginJWTKey))

encodedPayloadLength := base64.RawStdEncoding.EncodedLen(len(payload))
headerEnd := len(encodedJWTHeader)
dataStart := headerEnd + 1
dataEnd := dataStart + encodedPayloadLength
dataEnd := dataStart + base64.RawStdEncoding.EncodedLen(len(payload))
signatureStart := dataEnd + 1
signatureEnd := signatureStart + base64.RawStdEncoding.EncodedLen(signer.Size())

encodedJWT := make([]byte, signatureEnd)

copy(encodedJWT, encodedJWTHeader)
copy(encodedJWT[:headerEnd], encodedJWTHeader)
encodedJWT[headerEnd] = '.'
encodedJWT[dataEnd] = '.'

Expand Down

0 comments on commit e5822c7

Please sign in to comment.