diff --git a/README.md b/README.md index b49d85b..0b11a0a 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/adminapi.go b/adminapi.go index 0fc98c4..e293385 100644 --- a/adminapi.go +++ b/adminapi.go @@ -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) { diff --git a/go.mod b/go.mod index 8c07564..af9d85a 100644 --- a/go.mod +++ b/go.mod @@ -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 ( diff --git a/go.sum b/go.sum index 8377669..731ece0 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/jwt.go b/jwt.go index bd514b4..6304770 100644 --- a/jwt.go +++ b/jwt.go @@ -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)), }) @@ -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] = '.'