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
36 changes: 35 additions & 1 deletion internal/step_oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ const (
googleOAuthAuthorizationURL = "https://accounts.google.com/o/oauth2/v2/auth"
googleOAuthTokenURL = "https://oauth2.googleapis.com/token"
googleOAuthUserinfoURL = "https://openidconnect.googleapis.com/v1/userinfo"
facebookOAuthAuthorizationURL = "https://www.facebook.com/v18.0/dialog/oauth"
facebookOAuthTokenURL = "https://graph.facebook.com/v18.0/oauth/access_token"
facebookOAuthUserinfoURL = "https://graph.facebook.com/me?fields=id,email,name"
oauthDefaultReturnTo = "/auth/callback"
oauthDefaultStateTTL = 15 * time.Minute
oauthJSONBodyLimit = 1 << 20
)

var googleOAuthScopes = []string{"openid", "email", "profile"}
var facebookOAuthScopes = []string{"email", "public_profile"}

type oauthProviderConfigStep struct {
name string
Expand Down Expand Up @@ -290,7 +294,37 @@ func oauthProviderConfig(config map[string]any, provider string) (oauthProviderC
cfg.userinfoURL = override
}
return cfg, ""
case "facebook", "instagram", "x", "bluesky":
case "facebook":
cfg := oauthProviderConfigData{
provider: "facebook",
clientID: oauthString(config, "facebook_oauth_client_id"),
clientSecret: oauthString(config, "facebook_oauth_client_secret"),
redirectURL: oauthString(config, "facebook_oauth_redirect_url"),
authorizationURL: facebookOAuthAuthorizationURL,
tokenURL: facebookOAuthTokenURL,
userinfoURL: facebookOAuthUserinfoURL,
scopes: append([]string(nil), facebookOAuthScopes...),
}
if cfg.clientID == "" || cfg.clientSecret == "" || cfg.redirectURL == "" {
return cfg, "facebook oauth is not fully configured"
}
if override, ok := oauthEndpointURL(config, "facebook_oauth_authorization_url", facebookOAuthAuthorizationURL, "www.facebook.com"); !ok {
return cfg, "facebook authorization_url must use https and the expected Facebook hostname unless test endpoints are explicitly enabled"
} else {
cfg.authorizationURL = override
}
if override, ok := oauthEndpointURL(config, "facebook_oauth_token_url", facebookOAuthTokenURL, "graph.facebook.com"); !ok {
return cfg, "facebook token_url must use https and the expected Facebook hostname unless test endpoints are explicitly enabled"
} else {
cfg.tokenURL = override
}
if override, ok := oauthEndpointURL(config, "facebook_oauth_userinfo_url", facebookOAuthUserinfoURL, "graph.facebook.com"); !ok {
return cfg, "facebook userinfo_url must use https and the expected Facebook hostname unless test endpoints are explicitly enabled"
} else {
cfg.userinfoURL = override
}
return cfg, ""
case "instagram", "x", "bluesky":
return oauthProviderConfigData{provider: provider}, provider + " oauth provider is disabled in this release"
case "":
return oauthProviderConfigData{}, "missing oauth provider"
Expand Down
48 changes: 48 additions & 0 deletions internal/step_oauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,51 @@ func googleOAuthTestConfig(extra map[string]any) map[string]any {
}
return config
}

func facebookOAuthTestConfig(extra map[string]any) map[string]any {
config := map[string]any{
"facebook_oauth_client_id": "fb-client",
"facebook_oauth_client_secret": "fb-secret",
"facebook_oauth_redirect_url": "https://app.example.com/auth/facebook/callback",
}
for k, v := range extra {
config[k] = v
}
return config
}

func TestOAuthProviderConfig_FacebookAvailable(t *testing.T) {
step := newOAuthProviderConfigStep("test", facebookOAuthTestConfig(nil))
result, err := step.Execute(context.Background(), nil, nil, map[string]any{"provider": "facebook"}, nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Output["available"] != true {
t.Fatalf("expected available=true, got %v", result.Output["available"])
}
if result.Output["provider"] != "facebook" {
t.Fatalf("expected provider=facebook, got %v", result.Output["provider"])
}
if result.Output["client_id"] != "fb-client" {
t.Fatalf("expected client_id=fb-client, got %v", result.Output["client_id"])
}
if scopes, ok := result.Output["scopes"].([]string); !ok || len(scopes) == 0 {
t.Fatalf("expected scopes, got %v", result.Output["scopes"])
}
}

func TestOAuthProviderConfig_FacebookIncompleteUnavailable(t *testing.T) {
step := newOAuthProviderConfigStep("test", map[string]any{
"facebook_oauth_client_id": "fb-client",
})
result, err := step.Execute(context.Background(), nil, nil, map[string]any{"provider": "facebook"}, nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Output["available"] != false {
t.Fatalf("expected available=false, got %v", result.Output["available"])
}
if result.Output["disabled_reason"] == "" || result.Output["disabled_reason"] == nil {
t.Fatalf("expected disabled_reason, got %v", result.Output["disabled_reason"])
}
}
Loading