Add api-key based login

This commit is contained in:
Bernd Schoolmann 2024-01-04 21:53:38 +01:00
parent f36f99456b
commit 5af078092e
No known key found for this signature in database
10 changed files with 349 additions and 63 deletions

View File

@ -51,7 +51,11 @@ func sync(ctx context.Context, vault *vault.Vault, cfg *config.Config) bool {
token, err := cfg.GetToken()
if err == nil {
if token.AccessToken != "" {
bitwarden.RefreshToken(ctx, cfg)
refreshed := bitwarden.RefreshToken(ctx, cfg)
if !refreshed {
return false
}
userSymmetricKey, err := cfg.GetUserSymmetricKey()
if err != nil {
return false
@ -83,10 +87,17 @@ func ensureIsNotLocked(action Action) Action {
ctx1 := context.Background()
success := sync(ctx1, vault, cfg)
if err != nil || !success {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
} else {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: "Could not sync vault",
})
}
}
systemauth.CreatePinSession(*ctx)

View File

@ -55,6 +55,38 @@ func handleSetNotifications(request messages.IPCMessage, cfg *config.Config, vau
})
}
func handleSetClientID(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
clientID := messages.ParsePayload(request).(messages.SetClientIDRequest).Value
cfg.SetClientID(clientID)
err = cfg.WriteConfig()
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
}
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: true,
})
}
func handleSetClientSecret(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
clientSecret := messages.ParsePayload(request).(messages.SetClientSecretRequest).Value
cfg.SetClientSecret(clientSecret)
err = cfg.WriteConfig()
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
}
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: true,
})
}
func handleGetRuntimeConfig(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
return messages.IPCMessageFromPayload(messages.GetRuntimeConfigResponse{
UseMemguard: cfg.ConfigFile.RuntimeConfig.UseMemguard,
@ -68,4 +100,6 @@ func init() {
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetApiURLRequest{}), handleSetApiURL)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetNotificationsURLRequest{}), handleSetNotifications)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetRuntimeConfigRequest{}), handleGetRuntimeConfig)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetClientIDRequest{}), handleSetClientID)
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetClientSecretRequest{}), handleSetClientSecret)
}

View File

@ -32,7 +32,9 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
var masterKey crypto.MasterKey
var masterpasswordHash string
if req.Passwordless {
if secret, err := cfg.GetClientSecret(); err == nil && secret != "" {
token, masterKey, masterpasswordHash, err = bitwarden.LoginWithApiKey(ctx, req.Email, cfg, vault)
} else if req.Passwordless {
token, masterKey, masterpasswordHash, err = bitwarden.LoginWithDevice(ctx, req.Email, cfg, vault)
} else {
token, masterKey, masterpasswordHash, err = bitwarden.LoginWithMasterpassword(ctx, req.Email, cfg, vault)

View File

@ -38,11 +38,15 @@ type preLoginResponse struct {
}
type LoginResponseToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
Key string `json:"key"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
Key string `json:"key"`
Kdf int `json:"Kdf"`
KdfIterations int `json:"KdfIterations"`
KdfMemory int `json:"KdfMemory"`
KdfParallelism int `json:"KdfParallelism"`
}
const (
@ -64,6 +68,52 @@ func deviceType() string {
}
}
func LoginWithApiKey(ctx context.Context, email string, cfg *config.Config, vault *vault.Vault) (LoginResponseToken, crypto.MasterKey, string, error) {
clientID, err := cfg.GetClientID()
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not get client ID: %v", err), "", func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not get client ID: %v", err)
}
clientSecret, err := cfg.GetClientSecret()
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not get client secret: %v", err), "", func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not get client secret: %v", err)
}
values := urlValues(
"client_id", clientID,
"client_secret", clientSecret,
"scope", loginApiKeyScope,
"grant_type", "client_credentials",
"deviceType", deviceType(),
"deviceName", deviceName,
"deviceIdentifier", cfg.ConfigFile.DeviceUUID,
)
var loginResponseToken LoginResponseToken
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values)
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not login via API key: %v", err), "", func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via API key: %v", err)
}
password, err := pinentry.GetPassword("Bitwarden Password", "Enter your Bitwarden password")
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", err
}
masterKey, err := crypto.DeriveMasterKey([]byte(strings.Clone(password)), email, crypto.KDFConfig{Type: crypto.KDFType(loginResponseToken.Kdf), Iterations: uint32(loginResponseToken.KdfIterations), Memory: uint32(loginResponseToken.KdfMemory), Parallelism: uint32(loginResponseToken.KdfParallelism)})
if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not derive master key: %v", err), "", func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", err
}
hashedPassword := b64enc.EncodeToString(pbkdf2.Key(masterKey.GetBytes(), []byte(password), 1, 32, sha256.New))
authLog.Info("Logged in")
return loginResponseToken, masterKey, hashedPassword, nil
}
func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Config, vault *vault.Vault) (LoginResponseToken, crypto.MasterKey, string, error) {
var preLogin preLoginResponse
if err := authenticatedHTTPPost(ctx, cfg.ConfigFile.ApiUrl+"/accounts/prelogin", &preLogin, preLoginRequest{
@ -200,25 +250,70 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool {
fmt.Println("Could not get refresh token: ", err)
return false
}
if token.RefreshToken == "" {
authLog.Info("Refreshing using API Key")
clientID, err := cfg.GetClientID()
if err != nil {
authLog.Error("Could not get client ID: %s", err.Error())
return false
}
clientSecret, err := cfg.GetClientSecret()
if err != nil {
authLog.Error("Could not get client secret: %s", err.Error())
return false
}
var loginResponseToken LoginResponseToken
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, urlValues(
"grant_type", "refresh_token",
"refresh_token", token.RefreshToken,
"client_id", "connector",
))
if err != nil {
authLog.Error("Could not refresh token: %s", err.Error())
notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", func() {})
return false
if clientID != "" && clientSecret != "" {
values := urlValues(
"client_id", clientID,
"client_secret", clientSecret,
"scope", loginApiKeyScope,
"grant_type", "client_credentials",
"deviceType", deviceType(),
"deviceName", deviceName,
"deviceIdentifier", cfg.ConfigFile.DeviceUUID,
)
var loginResponseToken LoginResponseToken
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values)
if err != nil {
authLog.Error("Could not refresh token: %s", err.Error())
notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", func() {})
return false
}
cfg.SetToken(config.LoginToken{
AccessToken: loginResponseToken.AccessToken,
RefreshToken: "",
Key: loginResponseToken.Key,
TokenType: loginResponseToken.TokenType,
ExpiresIn: loginResponseToken.ExpiresIn,
})
} else {
authLog.Info("No api credentials set")
}
} else {
authLog.Info("Refreshing using refresh token")
var loginResponseToken LoginResponseToken
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, urlValues(
"grant_type", "refresh_token",
"refresh_token", token.RefreshToken,
"client_id", "connector",
))
if err != nil {
authLog.Error("Could not refresh token: %s", err.Error())
notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", func() {})
return false
}
cfg.SetToken(config.LoginToken{
AccessToken: loginResponseToken.AccessToken,
RefreshToken: loginResponseToken.RefreshToken,
Key: loginResponseToken.Key,
TokenType: loginResponseToken.TokenType,
ExpiresIn: loginResponseToken.ExpiresIn,
})
}
cfg.SetToken(config.LoginToken{
AccessToken: loginResponseToken.AccessToken,
RefreshToken: loginResponseToken.RefreshToken,
Key: loginResponseToken.Key,
TokenType: loginResponseToken.TokenType,
ExpiresIn: loginResponseToken.ExpiresIn,
})
authLog.Info("Token refreshed")

View File

@ -54,6 +54,8 @@ type ConfigFile struct {
IdentityUrl string
ApiUrl string
NotificationsUrl string
EncryptedClientID string
EncryptedClientSecret string
DeviceUUID string
ConfigKeyHash string
EncryptedToken string
@ -88,6 +90,8 @@ func DefaultConfig(useMemguard bool) Config {
IdentityUrl: "https://vault.bitwarden.com/identity",
ApiUrl: "https://vault.bitwarden.com/api",
NotificationsUrl: "https://notifications.bitwarden.com",
EncryptedClientID: "",
EncryptedClientSecret: "",
DeviceUUID: deviceUUID.String(),
ConfigKeyHash: "",
EncryptedToken: "",
@ -241,6 +245,82 @@ func (c *Config) SetToken(token LoginToken) error {
return nil
}
func (c *Config) GetClientID() (string, error) {
if c.IsLocked() {
return "", errors.New("config is locked")
}
if c.ConfigFile.EncryptedClientID == "" {
return "", nil
}
decrypted, err := c.decryptString(c.ConfigFile.EncryptedClientID)
if err != nil {
return "", err
}
return decrypted, nil
}
func (c *Config) SetClientID(clientID string) error {
if c.IsLocked() {
return errors.New("config is locked")
}
if clientID == "" {
c.ConfigFile.EncryptedClientID = ""
c.WriteConfig()
return nil
}
encryptedClientID, err := c.encryptString(clientID)
if err != nil {
return err
}
// c.mu.Lock()
c.ConfigFile.EncryptedClientID = encryptedClientID
// c.mu.Unlock()
c.WriteConfig()
return nil
}
func (c *Config) GetClientSecret() (string, error) {
if c.IsLocked() {
return "", errors.New("config is locked")
}
if c.ConfigFile.EncryptedClientSecret == "" {
return "", nil
}
decrypted, err := c.decryptString(c.ConfigFile.EncryptedClientSecret)
if err != nil {
return "", err
}
return decrypted, nil
}
func (c *Config) SetClientSecret(clientSecret string) error {
if c.IsLocked() {
return errors.New("config is locked")
}
if clientSecret == "" {
c.ConfigFile.EncryptedClientSecret = ""
c.WriteConfig()
return nil
}
encryptedClientSecret, err := c.encryptString(clientSecret)
if err != nil {
return err
}
// c.mu.Lock()
c.ConfigFile.EncryptedClientSecret = encryptedClientSecret
// c.mu.Unlock()
c.WriteConfig()
return nil
}
func (c *Config) GetUserSymmetricKey() ([]byte, error) {
if c.IsLocked() {
return []byte{}, errors.New("config is locked")

View File

@ -23,7 +23,7 @@ import (
const (
FullSyncInterval = 60 * time.Minute
TokenRefreshInterval = 30 * time.Minute
TokenRefreshInterval = 10 * time.Minute
)
var log = logging.GetLogger("Goldwarden", "Agent")
@ -248,6 +248,7 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
for {
time.Sleep(FullSyncInterval)
if !cfg.IsLocked() {
bitwarden.RefreshToken(ctx, &cfg)
token, err := cfg.GetToken()
if err != nil {
log.Warn("Could not get token: %s", err.Error())

View File

@ -106,6 +106,72 @@ var setNotificationsURLCmd = &cobra.Command{
},
}
var setApiClientIDCmd = &cobra.Command{
Use: "set-api-client-id",
Short: "Set the api client id",
Long: `Set the api client id.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
return
}
id := args[0]
request := messages.SetClientIDRequest{}
request.Value = id
result, err := commandClient.SendToAgent(request)
if err != nil {
handleSendToAgentError(err)
return
}
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
} else {
println("Setting api client id failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
}
},
}
var setApiSecretCmd = &cobra.Command{
Use: "set-api-client-secret",
Short: "Set the api secret",
Long: `Set the api secret.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
return
}
secret := args[0]
request := messages.SetClientSecretRequest{}
request.Value = secret
result, err := commandClient.SendToAgent(request)
if err != nil {
handleSendToAgentError(err)
return
}
switch result.(type) {
case messages.ActionResponse:
if result.(messages.ActionResponse).Success {
println("Done")
} else {
println("Setting api secret failed: " + result.(messages.ActionResponse).Message)
}
default:
println("Wrong IPC response type")
}
},
}
var getRuntimeConfigCmd = &cobra.Command{
Use: "get-runtime-config",
Short: "Get the runtime config",
@ -144,4 +210,6 @@ func init() {
configCmd.AddCommand(setIdentityURLCmd)
configCmd.AddCommand(setNotificationsURLCmd)
configCmd.AddCommand(getRuntimeConfigCmd)
configCmd.AddCommand(setApiClientIDCmd)
configCmd.AddCommand(setApiSecretCmd)
}

15
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/awnumar/memguard v0.22.4
github.com/google/uuid v1.5.0
github.com/gorilla/websocket v1.5.1
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5
github.com/lox/go-touchid v0.0.0-20170712105233-619cc8e578d0
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
github.com/mitchellh/go-ps v1.0.0
@ -23,22 +24,8 @@ require (
require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-text/typesetting v0.0.0-20230602202114-9797aefac433 // indirect
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5 // indirect
github.com/k0kubun/pp v2.4.0+incompatible // indirect
github.com/k0kubun/pp/v3 v3.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848 // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
require (

22
go.sum
View File

@ -1,11 +1,3 @@
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
gioui.org v0.1.0 h1:fEDY5A4+epOdzjCBYSUC4BzvjWqsjfqf5D6mskbthOs=
gioui.org v0.1.0/go.mod h1:a3hz8FyrPMkt899D9YrxMGtyRzpPrJpz1Lzbssn81vI=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y=
gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/LlamaNite/llamalog v0.2.1 h1:k9XugHmyQqJhCrogca808Jl2rrEKIWMtWyLKX+xX9Mg=
github.com/LlamaNite/llamalog v0.2.1/go.mod h1:zopgmWk8utZPfZCPa/uvQkv99Lan3pRrw/9inbIYZeo=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
@ -22,11 +14,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-text/typesetting v0.0.0-20230602202114-9797aefac433 h1:Pdyvqsfi1QYgFfZa4R8otBOtgO+CGyBDMEG8cM3jwvE=
github.com/go-text/typesetting v0.0.0-20230602202114-9797aefac433/go.mod h1:KmrpWuSMFcO2yjmyhGpnBGQHSKAoEgMTSSzvLDzCuEA=
github.com/go-text/typesetting-utils v0.0.0-20230412163830-89e4bcfa3ecc h1:9Kf84pnrmmjdRzZIkomfjowmGUhHs20jkrWYw/I6CYc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -71,8 +60,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/tink-crypto/tink-go/v2 v2.1.0 h1:QXFBguwMwTIaU17EgZpEJWsUSc60b1BAGTzBIoMdmok=
github.com/tink-crypto/tink-go/v2 v2.1.0/go.mod h1:y1TnYFt1i2eZVfx4OGc+C+EMp4CoKWAw2VSEuoicHHI=
github.com/twpayne/go-pinentry v0.3.0 h1:Rr+fEOZXmeItOb4thjeVaBWJKB9Xa/eojolycyF/26c=
@ -85,10 +73,6 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848 h1:LnDWUUS+bxOesHc+QBvFFmS4v0ZH+Vtg0EncMANwN9Q=
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -100,8 +84,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -14,6 +14,14 @@ type SetNotificationsURLRequest struct {
Value string
}
type SetClientIDRequest struct {
Value string
}
type SetClientSecretRequest struct {
Value string
}
type GetRuntimeConfigRequest struct{}
type GetRuntimeConfigResponse struct {
@ -67,4 +75,22 @@ func init() {
}
return req, nil
}, GetRuntimeConfigResponse{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SetClientIDRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, SetClientIDRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req SetClientSecretRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, SetClientSecretRequest{})
}