Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,14 @@ EOF
"GOTRUE_HOOK_SEND_EMAIL_SECRETS="+hook.Secrets.Value,
)
}
if hook := utils.Config.Auth.Hook.BeforeUserCreated; hook != nil && hook.Enabled {
env = append(
env,
"GOTRUE_HOOK_BEFORE_USER_CREATED_ENABLED=true",
"GOTRUE_HOOK_BEFORE_USER_CREATED_URI="+hook.URI,
"GOTRUE_HOOK_BEFORE_USER_CREATED_SECRETS="+hook.Secrets.Value,
)
}

if utils.Config.Auth.MFA.Phone.EnrollEnabled || utils.Config.Auth.MFA.Phone.VerifyEnabled {
env = append(
Expand Down
19 changes: 19 additions & 0 deletions pkg/config/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type (
CustomAccessToken *hookConfig `toml:"custom_access_token"`
SendSMS *hookConfig `toml:"send_sms"`
SendEmail *hookConfig `toml:"send_email"`
BeforeUserCreated *hookConfig `toml:"before_user_created"`
}

factorTypeConfiguration struct {
Expand Down Expand Up @@ -381,6 +382,14 @@ func (c *captcha) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {

func (h hook) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
// When local config is not set, we assume platform defaults should not change
if hook := h.BeforeUserCreated; hook != nil {
if body.HookBeforeUserCreatedEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled {
body.HookBeforeUserCreatedUri = nullable.NewNullableWithValue(hook.URI)
if len(hook.Secrets.SHA256) > 0 {
body.HookBeforeUserCreatedSecrets = nullable.NewNullableWithValue(hook.Secrets.Value)
}
}
}
if hook := h.CustomAccessToken; hook != nil {
if body.HookCustomAccessTokenEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled {
body.HookCustomAccessTokenUri = nullable.NewNullableWithValue(hook.URI)
Expand Down Expand Up @@ -425,6 +434,16 @@ func (h hook) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
}
func (h *hook) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
// When local config is not set, we assume platform defaults should not change
if hook := h.BeforeUserCreated; hook != nil {
// Ignore disabled hooks because their envs are not loaded
if hook.Enabled {
hook.URI = ValOrDefault(remoteConfig.HookBeforeUserCreatedUri, "")
if len(hook.Secrets.SHA256) > 0 {
hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookBeforeUserCreatedSecrets, "")
}
}
hook.Enabled = ValOrDefault(remoteConfig.HookBeforeUserCreatedEnabled, false)
}
if hook := h.CustomAccessToken; hook != nil {
// Ignore disabled hooks because their envs are not loaded
if hook.Enabled {
Expand Down
29 changes: 29 additions & 0 deletions pkg/config/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@ func TestHookDiff(t *testing.T) {
t.Run("local and remote enabled", func(t *testing.T) {
c := newWithDefaults()
c.Hook = hook{
BeforeUserCreated: &hookConfig{
Enabled: true,
URI: "http://example.com",
Secrets: Secret{
Value: "test-secret",
SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252",
},
},
CustomAccessToken: &hookConfig{
Enabled: true,
URI: "http://example.com",
Expand Down Expand Up @@ -254,6 +262,9 @@ func TestHookDiff(t *testing.T) {
}
// Run test
diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(true),
HookBeforeUserCreatedUri: nullable.NewNullableWithValue("http://example.com"),
HookBeforeUserCreatedSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"),
HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(true),
HookCustomAccessTokenUri: nullable.NewNullableWithValue("http://example.com"),
HookCustomAccessTokenSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"),
Expand All @@ -277,6 +288,9 @@ func TestHookDiff(t *testing.T) {
t.Run("local disabled remote enabled", func(t *testing.T) {
c := newWithDefaults()
c.Hook = hook{
BeforeUserCreated: &hookConfig{
Enabled: false,
},
CustomAccessToken: &hookConfig{
Enabled: false,
},
Expand All @@ -296,6 +310,9 @@ func TestHookDiff(t *testing.T) {
}
// Run test
diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(true),
HookBeforeUserCreatedUri: nullable.NewNullableWithValue("http://example.com"),
HookBeforeUserCreatedSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"),
HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(true),
HookCustomAccessTokenUri: nullable.NewNullableWithValue("http://example.com"),
HookCustomAccessTokenSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"),
Expand All @@ -318,6 +335,14 @@ func TestHookDiff(t *testing.T) {
t.Run("local enabled remote disabled", func(t *testing.T) {
c := newWithDefaults()
c.Hook = hook{
BeforeUserCreated: &hookConfig{
Enabled: true,
URI: "http://example.com",
Secrets: Secret{
Value: "test-secret",
SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252",
},
},
CustomAccessToken: &hookConfig{
Enabled: true,
URI: "http://example.com",
Expand Down Expand Up @@ -346,6 +371,8 @@ func TestHookDiff(t *testing.T) {
}
// Run test
diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(false),
HookBeforeUserCreatedUri: nullable.NewNullableWithValue("pg-functions://postgres/public/beforeUserCreated"),
HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(false),
HookCustomAccessTokenUri: nullable.NewNullableWithValue("pg-functions://postgres/public/customToken"),
HookSendSmsEnabled: nullable.NewNullableWithValue(false),
Expand All @@ -366,6 +393,7 @@ func TestHookDiff(t *testing.T) {
t.Run("local and remote disabled", func(t *testing.T) {
c := newWithDefaults()
c.Hook = hook{
BeforeUserCreated: &hookConfig{Enabled: false},
CustomAccessToken: &hookConfig{Enabled: false},
SendSMS: &hookConfig{Enabled: false},
SendEmail: &hookConfig{Enabled: false},
Expand All @@ -374,6 +402,7 @@ func TestHookDiff(t *testing.T) {
}
// Run test
diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(false),
HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(false),
HookSendSmsEnabled: nullable.NewNullableWithValue(false),
HookSendEmailEnabled: nullable.NewNullableWithValue(false),
Expand Down
9 changes: 9 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ func (a *auth) Clone() auth {
hook := *a.Hook.SendEmail
copy.Hook.SendEmail = &hook
}
if a.Hook.BeforeUserCreated != nil {
hook := *a.Hook.BeforeUserCreated
copy.Hook.BeforeUserCreated = &hook
}
copy.Sms.TestOTP = maps.Clone(a.Sms.TestOTP)
return copy
}
Expand Down Expand Up @@ -1161,6 +1165,11 @@ func (h *hook) validate() error {
return err
}
}
if hook := h.BeforeUserCreated; hook != nil {
if err := hook.validate("before_user_created"); err != nil {
return err
}
}
return nil
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/config/templates/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ max_frequency = "5s"
# Force log out if the user has been inactive longer than the specified duration.
# inactivity_timeout = "8h"

# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object.
# [auth.hook.before_user_created]
# enabled = true
# uri = "pg-functions://postgres/auth/before-user-created-hook"

# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
# [auth.hook.custom_access_token]
# enabled = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
diff remote[auth] local[auth]
--- remote[auth]
+++ local[auth]
@@ -24,19 +24,19 @@
@@ -24,23 +24,23 @@

[hook]
[hook.mfa_verification_attempt]
Expand All @@ -24,4 +24,9 @@ diff remote[auth] local[auth]
+enabled = false
uri = ""
secrets = ""
[hook.before_user_created]
-enabled = true
+enabled = false
uri = ""
secrets = ""

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
diff remote[auth] local[auth]
--- remote[auth]
+++ local[auth]
@@ -24,20 +24,20 @@
@@ -24,25 +24,25 @@

[hook]
[hook.mfa_verification_attempt]
Expand All @@ -27,5 +27,13 @@ diff remote[auth] local[auth]
+enabled = true
+uri = "pg-functions://postgres/public/sendEmail"
secrets = ""
[hook.before_user_created]
-enabled = false
-uri = "pg-functions://postgres/public/beforeUserCreated"
-secrets = ""
+enabled = true
+uri = "http://example.com"
+secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"

[mfa]
max_enrolled_factors = 0
5 changes: 5 additions & 0 deletions pkg/config/testdata/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ timebox = "24h"
# Force log out if the user has been inactive longer than the specified duration.
inactivity_timeout = "8h"

# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object.
[auth.hook.before_user_created]
enabled = true
uri = "pg-functions://postgres/auth/before-user-created-hook"

# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
[auth.hook.custom_access_token]
enabled = true
Expand Down