From 29ead2858166fc37ce19e443f102fe9cbd8c2fe8 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 9 May 2024 15:33:13 +0800 Subject: [PATCH 01/30] Move OAuth provider config to a separate package --- pkg/api/oauthrelyingparty/provider.go | 144 +++++++ pkg/api/oauthrelyingparty/registry.go | 15 + pkg/auth/handler/webapp/authflowv2/routes.go | 3 +- pkg/auth/handler/webapp/identity_test.go | 6 +- pkg/auth/webapp/authflow_routes.go | 3 +- .../webapp/wechat_redirect_uri_middleware.go | 7 +- pkg/lib/analytic/count.go | 4 +- .../declarative/data_identification.go | 13 +- .../declarative/data_oauth.go | 10 +- .../declarative/input_step_identify_test.go | 3 +- ...t_take_oauth_authorization_request_test.go | 3 +- .../declarative/utils_common.go | 5 +- pkg/lib/authn/identity/candidate.go | 39 +- pkg/lib/authn/identity/info.go | 10 +- pkg/lib/authn/identity/info_test.go | 7 +- pkg/lib/authn/identity/oauth/provider.go | 17 +- pkg/lib/authn/identity/oauth/store.go | 17 +- pkg/lib/authn/identity/oauth_identity.go | 17 +- pkg/lib/authn/identity/oauth_spec.go | 10 +- pkg/lib/authn/identity/service/service.go | 15 +- .../identity/service/service_mock_test.go | 26 +- .../authn/identity/service/service_test.go | 30 +- pkg/lib/authn/sso/adfs.go | 21 +- pkg/lib/authn/sso/adfs_test.go | 12 +- pkg/lib/authn/sso/apple.go | 26 +- pkg/lib/authn/sso/apple_test.go | 10 +- pkg/lib/authn/sso/azureadb2c.go | 24 +- pkg/lib/authn/sso/azureadb2c_test.go | 14 +- pkg/lib/authn/sso/azureadv2.go | 22 +- pkg/lib/authn/sso/azureadv2_test.go | 12 +- pkg/lib/authn/sso/facebook.go | 18 +- pkg/lib/authn/sso/facebook_test.go | 10 +- pkg/lib/authn/sso/github.go | 18 +- pkg/lib/authn/sso/github_test.go | 10 +- pkg/lib/authn/sso/google.go | 18 +- pkg/lib/authn/sso/google_test.go | 10 +- pkg/lib/authn/sso/linkedin.go | 18 +- pkg/lib/authn/sso/linkedin_test.go | 10 +- pkg/lib/authn/sso/oauth_provider.go | 33 +- pkg/lib/authn/sso/wechat.go | 20 +- pkg/lib/authn/sso/wechat_test.go | 10 +- pkg/lib/config/config.go | 43 +-- pkg/lib/config/feature_identity.go | 39 ++ pkg/lib/config/identity.go | 360 +----------------- pkg/lib/config/secret.go | 5 +- pkg/lib/config/testdata/config_tests.yaml | 72 +--- pkg/lib/facade/coordinator.go | 26 +- .../nodes/use_identity_oauth_provider.go | 34 +- .../nodes/use_identity_oauth_user_info.go | 9 +- pkg/lib/oauthrelyingparty/adfs/provider.go | 74 ++++ pkg/lib/oauthrelyingparty/apple/provider.go | 91 +++++ .../oauthrelyingparty/azureadb2c/provider.go | 91 +++++ .../oauthrelyingparty/azureadv2/provider.go | 86 +++++ pkg/lib/oauthrelyingparty/builtin.go | 34 ++ .../oauthrelyingparty/facebook/provider.go | 74 ++++ pkg/lib/oauthrelyingparty/github/provider.go | 67 ++++ pkg/lib/oauthrelyingparty/google/provider.go | 70 ++++ .../oauthrelyingparty/linkedin/provider.go | 74 ++++ .../oauthrelyingpartyutil/claim_config.go | 17 + pkg/lib/oauthrelyingparty/wechat/provider.go | 119 ++++++ 60 files changed, 1350 insertions(+), 755 deletions(-) create mode 100644 pkg/api/oauthrelyingparty/provider.go create mode 100644 pkg/api/oauthrelyingparty/registry.go create mode 100644 pkg/lib/oauthrelyingparty/adfs/provider.go create mode 100644 pkg/lib/oauthrelyingparty/apple/provider.go create mode 100644 pkg/lib/oauthrelyingparty/azureadb2c/provider.go create mode 100644 pkg/lib/oauthrelyingparty/azureadv2/provider.go create mode 100644 pkg/lib/oauthrelyingparty/builtin.go create mode 100644 pkg/lib/oauthrelyingparty/facebook/provider.go create mode 100644 pkg/lib/oauthrelyingparty/github/provider.go create mode 100644 pkg/lib/oauthrelyingparty/google/provider.go create mode 100644 pkg/lib/oauthrelyingparty/linkedin/provider.go create mode 100644 pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go create mode 100644 pkg/lib/oauthrelyingparty/wechat/provider.go diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go new file mode 100644 index 0000000000..a39156c4e7 --- /dev/null +++ b/pkg/api/oauthrelyingparty/provider.go @@ -0,0 +1,144 @@ +package oauthrelyingparty + +import ( + "fmt" +) + +type ProviderConfig map[string]interface{} + +func (c ProviderConfig) MustGetProvider() Provider { + typ := c.Type() + p, ok := registry[typ] + if !ok { + panic(fmt.Errorf("oauth provider not in registry: %v", typ)) + } + return p +} + +func (c ProviderConfig) Alias() string { + alias, _ := c["alias"].(string) + return alias +} + +func (c ProviderConfig) Type() string { + typ, _ := c["type"].(string) + return typ +} + +func (c ProviderConfig) ClientID() string { + client_id, _ := c["client_id"].(string) + return client_id +} + +func (c ProviderConfig) SetDefaultsModifyDisabledFalse() { + _, ok := c["modify_disabled"].(bool) + if !ok { + c["modify_disabled"] = false + } +} + +func (c ProviderConfig) ModifyDisabled() bool { + modify_disabled, _ := c["modify_disabled"].(bool) + return modify_disabled +} + +func (c ProviderConfig) SetDefaultsEmailClaimConfig(claim ProviderClaimConfig) { + claims, ok := c["claims"].(map[string]interface{}) + if !ok { + claims = map[string]interface{}{} + c["claims"] = claims + } + + email, ok := claims["email"].(map[string]interface{}) + if !ok { + claims["email"] = map[string]interface{}(claim) + } else { + if _, ok := email["assume_verified"].(bool); !ok { + email["assume_verified"] = claim.AssumeVerified() + } + if _, ok := email["required"].(bool); !ok { + email["required"] = claim.Required() + } + } +} + +func (c ProviderConfig) EmailClaimConfig() ProviderClaimConfig { + claims, ok := c["claims"].(map[string]interface{}) + if !ok { + return ProviderClaimConfig{} + } + email, ok := claims["email"].(map[string]interface{}) + if !ok { + return ProviderClaimConfig{} + } + return ProviderClaimConfig(email) +} + +func (c ProviderConfig) SetDefaults() { + provider := c.MustGetProvider() + provider.SetDefaults(c) +} + +func (c ProviderConfig) Scope() []string { + provider := c.MustGetProvider() + return provider.Scope(c) +} + +func (c ProviderConfig) ProviderID() ProviderID { + provider := c.MustGetProvider() + return provider.ProviderID(c) +} + +type ProviderClaimConfig map[string]interface{} + +func (c ProviderClaimConfig) AssumeVerified() bool { + b, _ := c["assume_verified"].(bool) + return b +} + +func (c ProviderClaimConfig) Required() bool { + b, _ := c["required"].(bool) + return b +} + +// ProviderID combining with a subject ID identifies an user from an external system. +type ProviderID struct { + Type string + Keys map[string]interface{} +} + +func NewProviderID(typ string, keys map[string]interface{}) ProviderID { + id := ProviderID{ + Keys: make(map[string]interface{}), + } + id.Type = typ + for k, v := range keys { + id.Keys[k] = v + } + return id +} + +func (i ProviderID) Equal(that ProviderID) bool { + if i.Type != that.Type { + return false + } + if len(i.Keys) != len(that.Keys) { + return false + } + for key, thisValue := range i.Keys { + thatValue, ok := that.Keys[key] + if !ok { + return false + } + if thisValue != thatValue { + return false + } + } + return true +} + +type Provider interface { + SetDefaults(cfg ProviderConfig) + ProviderID(cfg ProviderConfig) ProviderID + Scope(cfg ProviderConfig) []string +} diff --git a/pkg/api/oauthrelyingparty/registry.go b/pkg/api/oauthrelyingparty/registry.go new file mode 100644 index 0000000000..6996b56c2f --- /dev/null +++ b/pkg/api/oauthrelyingparty/registry.go @@ -0,0 +1,15 @@ +package oauthrelyingparty + +import ( + "fmt" +) + +var registry = map[string]Provider{} + +func RegisterProvider(typ string, provider Provider) { + _, ok := registry[typ] + if ok { + panic(fmt.Errorf("oauth provider is already registered: %v", typ)) + } + registry[typ] = provider +} diff --git a/pkg/auth/handler/webapp/authflowv2/routes.go b/pkg/auth/handler/webapp/authflowv2/routes.go index 83edbd605d..8892fdc9d4 100644 --- a/pkg/auth/handler/webapp/authflowv2/routes.go +++ b/pkg/auth/handler/webapp/authflowv2/routes.go @@ -16,6 +16,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/otp" "github.com/authgear/authgear-server/pkg/lib/authn/user" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) const ( @@ -282,7 +283,7 @@ func (n *AuthflowV2Navigator) navigateStepIdentify(s *webapp.AuthflowScreenWithF data := s.StateTokenFlowResponse.Action.Data.(declarative.OAuthData) switch data.OAuthProviderType { - case config.OAuthSSOProviderTypeWechat: + case wechat.Type: s.Advance(AuthflowV2RouteWechat, result) default: authorizationURL, _ := url.Parse(data.OAuthAuthorizationURL) diff --git a/pkg/auth/handler/webapp/identity_test.go b/pkg/auth/handler/webapp/identity_test.go index 80fc2604ef..61dda06131 100644 --- a/pkg/auth/handler/webapp/identity_test.go +++ b/pkg/auth/handler/webapp/identity_test.go @@ -6,9 +6,9 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/config" ) func TestIdentitiesDisplayName(t *testing.T) { @@ -22,7 +22,7 @@ func TestIdentitiesDisplayName(t *testing.T) { oauthProviderIdentity := &identity.Info{ Type: model.IdentityTypeOAuth, OAuth: &identity.OAuth{ - ProviderID: config.ProviderID{ + ProviderID: oauthrelyingparty.ProviderID{ Type: "provider", }, Claims: map[string]interface{}{ @@ -34,7 +34,7 @@ func TestIdentitiesDisplayName(t *testing.T) { oauthProviderIdentityWithStandardClaims := &identity.Info{ Type: model.IdentityTypeOAuth, OAuth: &identity.OAuth{ - ProviderID: config.ProviderID{ + ProviderID: oauthrelyingparty.ProviderID{ Type: "provider2", }, Claims: map[string]interface{}{}, diff --git a/pkg/auth/webapp/authflow_routes.go b/pkg/auth/webapp/authflow_routes.go index efa7be4a50..12910c48c2 100644 --- a/pkg/auth/webapp/authflow_routes.go +++ b/pkg/auth/webapp/authflow_routes.go @@ -14,6 +14,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/otp" "github.com/authgear/authgear-server/pkg/lib/authn/user" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) const ( @@ -261,7 +262,7 @@ func (n *AuthflowNavigator) navigateStepIdentify(s *AuthflowScreenWithFlowRespon data := s.StateTokenFlowResponse.Action.Data.(declarative.OAuthData) switch data.OAuthProviderType { - case config.OAuthSSOProviderTypeWechat: + case wechat.Type: s.Advance(AuthflowRouteWechat, result) default: authorizationURL, _ := url.Parse(data.OAuthAuthorizationURL) diff --git a/pkg/auth/webapp/wechat_redirect_uri_middleware.go b/pkg/auth/webapp/wechat_redirect_uri_middleware.go index eed53d79b1..633ed1a64b 100644 --- a/pkg/auth/webapp/wechat_redirect_uri_middleware.go +++ b/pkg/auth/webapp/wechat_redirect_uri_middleware.go @@ -6,6 +6,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/lib/config" + oauthrelyingpartywechat "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/wechat" ) @@ -65,7 +66,7 @@ func (m *WeChatRedirectURIMiddleware) Handle(next http.Handler) http.Handler { func (m *WeChatRedirectURIMiddleware) isWechatEnabled() bool { for _, providerConfig := range m.IdentityConfig.OAuth.Providers { - if providerConfig.Type == config.OAuthSSOProviderTypeWechat { + if providerConfig.Type() == oauthrelyingpartywechat.Type { return true } } @@ -82,8 +83,8 @@ func (m *WeChatRedirectURIMiddleware) populateWechatRedirectURI( // Validate x_wechat_redirect_uri valid := false for _, providerConfig := range m.IdentityConfig.OAuth.Providers { - if providerConfig.Type == config.OAuthSSOProviderTypeWechat { - for _, allowed := range providerConfig.WeChatRedirectURIs { + if providerConfig.Type() == oauthrelyingpartywechat.Type { + for _, allowed := range oauthrelyingpartywechat.ProviderConfig(providerConfig).WechatRedirectURIs() { if weChatRedirectURI == allowed { valid = true } diff --git a/pkg/lib/analytic/count.go b/pkg/lib/analytic/count.go index ff8edde158..afce7323c1 100644 --- a/pkg/lib/analytic/count.go +++ b/pkg/lib/analytic/count.go @@ -5,7 +5,7 @@ import ( "time" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/util/uuid" ) @@ -40,7 +40,7 @@ func init() { string(typ), fmt.Sprintf(DailySignupWithLoginIDCountType, typ), }) } - for _, typ := range config.OAuthSSOProviderTypes { + for _, typ := range oauthrelyingparty.BuiltinProviderTypes { DailySignupCountTypeByMethods = append(DailySignupCountTypeByMethods, &DailySignupCountTypeByMethod{ string(typ), fmt.Sprintf(DailySignupWithOAuthCountType, typ), }) diff --git a/pkg/lib/authenticationflow/declarative/data_identification.go b/pkg/lib/authenticationflow/declarative/data_identification.go index cc8cca10ea..44ededacc5 100644 --- a/pkg/lib/authenticationflow/declarative/data_identification.go +++ b/pkg/lib/authenticationflow/declarative/data_identification.go @@ -5,6 +5,7 @@ import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) type IdentificationData struct { @@ -25,11 +26,11 @@ type IdentificationOption struct { Identification config.AuthenticationFlowIdentification `json:"identification"` // ProviderType is specific to OAuth. - ProviderType config.OAuthSSOProviderType `json:"provider_type,omitempty"` + ProviderType string `json:"provider_type,omitempty"` // Alias is specific to OAuth. Alias string `json:"alias,omitempty"` // WechatAppType is specific to OAuth. - WechatAppType config.OAuthSSOWeChatAppType `json:"wechat_app_type,omitempty"` + WechatAppType wechat.AppType `json:"wechat_app_type,omitempty"` // WebAuthnRequestOptions is specific to Passkey. RequestOptions *model.WebAuthnRequestOptions `json:"request_options,omitempty"` @@ -50,12 +51,12 @@ func NewIdentificationOptionLoginID(i config.AuthenticationFlowIdentification) I func NewIdentificationOptionsOAuth(oauthConfig *config.OAuthSSOConfig, oauthFeatureConfig *config.OAuthSSOProvidersFeatureConfig) []IdentificationOption { output := []IdentificationOption{} for _, p := range oauthConfig.Providers { - if !identity.IsOAuthSSOProviderTypeDisabled(p.Type, oauthFeatureConfig) { + if !identity.IsOAuthSSOProviderTypeDisabled(p, oauthFeatureConfig) { output = append(output, IdentificationOption{ Identification: config.AuthenticationFlowIdentificationOAuth, - ProviderType: p.Type, - Alias: p.Alias, - WechatAppType: p.AppType, + ProviderType: p.Type(), + Alias: p.Alias(), + WechatAppType: wechat.ProviderConfig(p).AppType(), }) } } diff --git a/pkg/lib/authenticationflow/declarative/data_oauth.go b/pkg/lib/authenticationflow/declarative/data_oauth.go index dece6acbfb..c1d2797112 100644 --- a/pkg/lib/authenticationflow/declarative/data_oauth.go +++ b/pkg/lib/authenticationflow/declarative/data_oauth.go @@ -2,15 +2,15 @@ package declarative import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) type OAuthData struct { TypedData - Alias string `json:"alias,omitempty"` - OAuthProviderType config.OAuthSSOProviderType `json:"oauth_provider_type,omitempty"` - OAuthAuthorizationURL string `json:"oauth_authorization_url,omitempty"` - WechatAppType config.OAuthSSOWeChatAppType `json:"wechat_app_type,omitempty"` + Alias string `json:"alias,omitempty"` + OAuthProviderType string `json:"oauth_provider_type,omitempty"` + OAuthAuthorizationURL string `json:"oauth_authorization_url,omitempty"` + WechatAppType wechat.AppType `json:"wechat_app_type,omitempty"` } var _ authflow.Data = OAuthData{} diff --git a/pkg/lib/authenticationflow/declarative/input_step_identify_test.go b/pkg/lib/authenticationflow/declarative/input_step_identify_test.go index 7d468d3924..45d2e94670 100644 --- a/pkg/lib/authenticationflow/declarative/input_step_identify_test.go +++ b/pkg/lib/authenticationflow/declarative/input_step_identify_test.go @@ -7,6 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) func TestInputSchemaStepIdentify(t *testing.T) { @@ -36,7 +37,7 @@ func TestInputSchemaStepIdentify(t *testing.T) { { Identification: config.AuthenticationFlowIdentificationOAuth, Alias: "wechat_mobile", - WechatAppType: config.OAuthSSOWeChatAppTypeMobile, + WechatAppType: wechat.AppTypeMobile, }, { Identification: config.AuthenticationFlowIdentificationPasskey, diff --git a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request_test.go b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request_test.go index 83f4c022ee..e6954be071 100644 --- a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request_test.go +++ b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request_test.go @@ -7,6 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) func TestInputSchemaTakeOAuthAuthorizationRequest(t *testing.T) { @@ -27,7 +28,7 @@ func TestInputSchemaTakeOAuthAuthorizationRequest(t *testing.T) { { Identification: config.AuthenticationFlowIdentificationOAuth, Alias: "wechat_mobile", - WechatAppType: config.OAuthSSOWeChatAppTypeMobile, + WechatAppType: wechat.AppTypeMobile, }, }, }, ` diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 6a38330ff4..8d915e3658 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -15,6 +15,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/mail" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/lib/uiparam" "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/phone" @@ -743,9 +744,9 @@ func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAut data = NewOAuthData(OAuthData{ Alias: opts.Alias, - OAuthProviderType: oauthProvider.Config().Type, + OAuthProviderType: oauthProvider.Config().Type(), OAuthAuthorizationURL: authorizationURL, - WechatAppType: oauthProvider.Config().AppType, + WechatAppType: wechat.ProviderConfig(oauthProvider.Config()).AppType(), }) return } diff --git a/pkg/lib/authn/identity/candidate.go b/pkg/lib/authn/identity/candidate.go index b64069c2e6..bd5910f16a 100644 --- a/pkg/lib/authn/identity/candidate.go +++ b/pkg/lib/authn/identity/candidate.go @@ -1,10 +1,10 @@ package identity import ( - "fmt" - "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) type Candidate map[string]interface{} @@ -27,16 +27,16 @@ const ( CandidateKeyModifyDisabled = "modify_disabled" ) -func NewOAuthCandidate(c *config.OAuthSSOProviderConfig) Candidate { +func NewOAuthCandidate(cfg oauthrelyingparty.ProviderConfig) Candidate { return Candidate{ CandidateKeyIdentityID: "", CandidateKeyType: string(model.IdentityTypeOAuth), - CandidateKeyProviderType: string(c.Type), - CandidateKeyProviderAlias: c.Alias, + CandidateKeyProviderType: string(cfg.Type()), + CandidateKeyProviderAlias: cfg.Alias(), CandidateKeyProviderSubjectID: "", - CandidateKeyProviderAppType: string(c.AppType), + CandidateKeyProviderAppType: string(wechat.ProviderConfig(cfg).AppType()), CandidateKeyDisplayID: "", - CandidateKeyModifyDisabled: *c.ModifyDisabled, + CandidateKeyModifyDisabled: cfg.ModifyDisabled(), } } @@ -60,27 +60,6 @@ func NewSIWECandidate() Candidate { } } -func IsOAuthSSOProviderTypeDisabled(typ config.OAuthSSOProviderType, featureConfig *config.OAuthSSOProvidersFeatureConfig) bool { - switch typ { - case config.OAuthSSOProviderTypeGoogle: - return featureConfig.Google.Disabled - case config.OAuthSSOProviderTypeFacebook: - return featureConfig.Facebook.Disabled - case config.OAuthSSOProviderTypeGithub: - return featureConfig.Github.Disabled - case config.OAuthSSOProviderTypeLinkedIn: - return featureConfig.LinkedIn.Disabled - case config.OAuthSSOProviderTypeAzureADv2: - return featureConfig.Azureadv2.Disabled - case config.OAuthSSOProviderTypeAzureADB2C: - return featureConfig.Azureadb2c.Disabled - case config.OAuthSSOProviderTypeADFS: - return featureConfig.ADFS.Disabled - case config.OAuthSSOProviderTypeApple: - return featureConfig.Apple.Disabled - case config.OAuthSSOProviderTypeWechat: - return featureConfig.Wechat.Disabled - default: - panic(fmt.Sprintf("node: unknown oauth sso type: %T", typ)) - } +func IsOAuthSSOProviderTypeDisabled(cfg oauthrelyingparty.ProviderConfig, featureConfig *config.OAuthSSOProvidersFeatureConfig) bool { + return featureConfig.IsDisabled(cfg) } diff --git a/pkg/lib/authn/identity/info.go b/pkg/lib/authn/identity/info.go index ebfd8c45a5..5ab2a3374c 100644 --- a/pkg/lib/authn/identity/info.go +++ b/pkg/lib/authn/identity/info.go @@ -5,6 +5,7 @@ import ( "time" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -282,17 +283,18 @@ func (i *Info) ModifyDisabled(c *config.IdentityConfig) bool { return *keyConfig.ModifyDisabled case model.IdentityTypeOAuth: alias := i.OAuth.ProviderAlias - var providerConfig *config.OAuthSSOProviderConfig + var providerConfig oauthrelyingparty.ProviderConfig for _, pc := range c.OAuth.Providers { - if pc.Alias == alias { + pcAlias := pc.Alias() + if pcAlias == alias { pcc := pc - providerConfig = &pcc + providerConfig = pcc } } if providerConfig == nil { return true } - return *providerConfig.ModifyDisabled + return providerConfig.ModifyDisabled() case model.IdentityTypeAnonymous: // modify_disabled is only applicable to login_id and oauth. // So we return false here. diff --git a/pkg/lib/authn/identity/info_test.go b/pkg/lib/authn/identity/info_test.go index 4242c5f5f9..bd98ecdb11 100644 --- a/pkg/lib/authn/identity/info_test.go +++ b/pkg/lib/authn/identity/info_test.go @@ -5,9 +5,10 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" ) func TestInfoJSON(t *testing.T) { @@ -60,7 +61,7 @@ func TestInfoJSON(t *testing.T) { CreatedAt: time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), - ProviderID: config.ProviderID{ + ProviderID: oauthrelyingparty.ProviderID{ Type: "provider", Keys: map[string]interface{}{ "client_id": "client_id", diff --git a/pkg/lib/authn/identity/oauth/provider.go b/pkg/lib/authn/identity/oauth/provider.go index ea53135229..d21c1ef180 100644 --- a/pkg/lib/authn/identity/oauth/provider.go +++ b/pkg/lib/authn/identity/oauth/provider.go @@ -3,6 +3,7 @@ package oauth import ( "sort" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" @@ -50,12 +51,12 @@ func (p *Provider) Get(userID, id string) (*identity.OAuth, error) { return p.Store.Get(userID, id) } -func (p *Provider) GetByProviderSubject(provider config.ProviderID, subjectID string) (*identity.OAuth, error) { - return p.Store.GetByProviderSubject(provider, subjectID) +func (p *Provider) GetByProviderSubject(providerID oauthrelyingparty.ProviderID, subjectID string) (*identity.OAuth, error) { + return p.Store.GetByProviderSubject(providerID, subjectID) } -func (p *Provider) GetByUserProvider(userID string, provider config.ProviderID) (*identity.OAuth, error) { - return p.Store.GetByUserProvider(userID, provider) +func (p *Provider) GetByUserProvider(userID string, providerID oauthrelyingparty.ProviderID) (*identity.OAuth, error) { + return p.Store.GetByUserProvider(userID, providerID) } func (p *Provider) GetMany(ids []string) ([]*identity.OAuth, error) { @@ -64,7 +65,7 @@ func (p *Provider) GetMany(ids []string) ([]*identity.OAuth, error) { func (p *Provider) New( userID string, - provider config.ProviderID, + providerID oauthrelyingparty.ProviderID, subjectID string, profile map[string]interface{}, claims map[string]interface{}, @@ -72,7 +73,7 @@ func (p *Provider) New( i := &identity.OAuth{ ID: uuid.New(), UserID: userID, - ProviderID: provider, + ProviderID: providerID, ProviderSubjectID: subjectID, UserProfile: profile, Claims: claims, @@ -81,8 +82,8 @@ func (p *Provider) New( alias := "" for _, providerConfig := range p.IdentityConfig.OAuth.Providers { providerID := providerConfig.ProviderID() - if providerID.Equal(&i.ProviderID) { - alias = providerConfig.Alias + if providerID.Equal(i.ProviderID) { + alias = providerConfig.Alias() } } if alias != "" { diff --git a/pkg/lib/authn/identity/oauth/store.go b/pkg/lib/authn/identity/oauth/store.go index c63477f6c0..b3021d081e 100644 --- a/pkg/lib/authn/identity/oauth/store.go +++ b/pkg/lib/authn/identity/oauth/store.go @@ -10,6 +10,7 @@ import ( "github.com/lib/pq" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db" @@ -75,8 +76,8 @@ func (s *Store) scan(scn db.Scanner) (*identity.OAuth, error) { alias := "" for _, providerConfig := range s.IdentityConfig.OAuth.Providers { providerID := providerConfig.ProviderID() - if providerID.Equal(&i.ProviderID) { - alias = providerConfig.Alias + if providerID.Equal(i.ProviderID) { + alias = providerConfig.Alias() } } if alias != "" { @@ -182,15 +183,15 @@ func (s *Store) Get(userID string, id string) (*identity.OAuth, error) { return s.scan(rows) } -func (s *Store) GetByProviderSubject(provider config.ProviderID, subjectID string) (*identity.OAuth, error) { - providerKeys, err := json.Marshal(provider.Keys) +func (s *Store) GetByProviderSubject(providerID oauthrelyingparty.ProviderID, subjectID string) (*identity.OAuth, error) { + providerKeys, err := json.Marshal(providerID.Keys) if err != nil { return nil, err } q := s.selectQuery().Where( "o.provider_type = ? AND o.provider_keys = ? AND o.provider_user_id = ?", - provider.Type, providerKeys, subjectID) + providerID.Type, providerKeys, subjectID) rows, err := s.SQLExecutor.QueryRowWith(q) if err != nil { return nil, err @@ -199,15 +200,15 @@ func (s *Store) GetByProviderSubject(provider config.ProviderID, subjectID strin return s.scan(rows) } -func (s *Store) GetByUserProvider(userID string, provider config.ProviderID) (*identity.OAuth, error) { - providerKeys, err := json.Marshal(provider.Keys) +func (s *Store) GetByUserProvider(userID string, providerID oauthrelyingparty.ProviderID) (*identity.OAuth, error) { + providerKeys, err := json.Marshal(providerID.Keys) if err != nil { return nil, err } q := s.selectQuery().Where( "o.provider_type = ? AND o.provider_keys = ? AND p.user_id = ?", - provider.Type, providerKeys, userID) + providerID.Type, providerKeys, userID) rows, err := s.SQLExecutor.QueryRowWith(q) if err != nil { return nil, err diff --git a/pkg/lib/authn/identity/oauth_identity.go b/pkg/lib/authn/identity/oauth_identity.go index cd4b699838..3b859712a0 100644 --- a/pkg/lib/authn/identity/oauth_identity.go +++ b/pkg/lib/authn/identity/oauth_identity.go @@ -4,20 +4,21 @@ import ( "time" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/mail" "github.com/authgear/authgear-server/pkg/util/phone" ) type OAuth struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - UserID string `json:"user_id"` - ProviderID config.ProviderID `json:"provider_id"` - ProviderSubjectID string `json:"provider_subject_id"` - UserProfile map[string]interface{} `json:"user_profile,omitempty"` - Claims map[string]interface{} `json:"claims,omitempty"` + ID string `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` + ProviderID oauthrelyingparty.ProviderID `json:"provider_id"` + ProviderSubjectID string `json:"provider_subject_id"` + UserProfile map[string]interface{} `json:"user_profile,omitempty"` + Claims map[string]interface{} `json:"claims,omitempty"` // This is a derived field and NOT persisted to database. // We still include it in JSON serialization so it can be persisted in the graph. ProviderAlias string `json:"provider_alias,omitempty"` diff --git a/pkg/lib/authn/identity/oauth_spec.go b/pkg/lib/authn/identity/oauth_spec.go index 7279234ed6..009ad266cc 100644 --- a/pkg/lib/authn/identity/oauth_spec.go +++ b/pkg/lib/authn/identity/oauth_spec.go @@ -1,12 +1,12 @@ package identity import ( - "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" ) type OAuthSpec struct { - ProviderID config.ProviderID `json:"provider_id"` - SubjectID string `json:"subject_id"` - RawProfile map[string]interface{} `json:"raw_profile,omitempty"` - StandardClaims map[string]interface{} `json:"standard_claims,omitempty"` + ProviderID oauthrelyingparty.ProviderID `json:"provider_id"` + SubjectID string `json:"subject_id"` + RawProfile map[string]interface{} `json:"raw_profile,omitempty"` + StandardClaims map[string]interface{} `json:"standard_claims,omitempty"` } diff --git a/pkg/lib/authn/identity/service/service.go b/pkg/lib/authn/identity/service/service.go index f0da7875c9..4d547f8f5e 100644 --- a/pkg/lib/authn/identity/service/service.go +++ b/pkg/lib/authn/identity/service/service.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" "github.com/authgear/authgear-server/pkg/lib/config" @@ -32,13 +33,13 @@ type OAuthIdentityProvider interface { Get(userID, id string) (*identity.OAuth, error) GetMany(ids []string) ([]*identity.OAuth, error) List(userID string) ([]*identity.OAuth, error) - GetByProviderSubject(provider config.ProviderID, subjectID string) (*identity.OAuth, error) - GetByUserProvider(userID string, provider config.ProviderID) (*identity.OAuth, error) + GetByProviderSubject(providerID oauthrelyingparty.ProviderID, subjectID string) (*identity.OAuth, error) + GetByUserProvider(userID string, providerID oauthrelyingparty.ProviderID) (*identity.OAuth, error) ListByClaim(name string, value string) ([]*identity.OAuth, error) ListByClaimJSONPointer(pointer jsonpointer.T, value string) ([]*identity.OAuth, error) New( userID string, - provider config.ProviderID, + providerID oauthrelyingparty.ProviderID, subjectID string, profile map[string]interface{}, claims map[string]interface{}, @@ -844,14 +845,14 @@ func (s *Service) listOAuthCandidates(oauths []*identity.OAuth) []identity.Candi out := []identity.Candidate{} for _, providerConfig := range s.Identity.OAuth.Providers { pc := providerConfig - if identity.IsOAuthSSOProviderTypeDisabled(pc.Type, s.IdentityFeatureConfig.OAuth.Providers) { + if identity.IsOAuthSSOProviderTypeDisabled(pc, s.IdentityFeatureConfig.OAuth.Providers) { continue } configProviderID := pc.ProviderID() - candidate := identity.NewOAuthCandidate(&pc) + candidate := identity.NewOAuthCandidate(pc) matched := false for _, iden := range oauths { - if iden.ProviderID.Equal(&configProviderID) { + if iden.ProviderID.Equal(configProviderID) { matched = true candidate[identity.CandidateKeyIdentityID] = iden.ID candidate[identity.CandidateKeyProviderSubjectID] = string(iden.ProviderSubjectID) @@ -859,7 +860,7 @@ func (s *Service) listOAuthCandidates(oauths []*identity.OAuth) []identity.Candi } } canAppend := true - if *providerConfig.ModifyDisabled && !matched { + if providerConfig.ModifyDisabled() && !matched { canAppend = false } if canAppend { diff --git a/pkg/lib/authn/identity/service/service_mock_test.go b/pkg/lib/authn/identity/service/service_mock_test.go index 92c21697a1..5da7e1271d 100644 --- a/pkg/lib/authn/identity/service/service_mock_test.go +++ b/pkg/lib/authn/identity/service/service_mock_test.go @@ -7,9 +7,9 @@ package service import ( reflect "reflect" + oauthrelyingparty "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" identity "github.com/authgear/authgear-server/pkg/lib/authn/identity" loginid "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" - config "github.com/authgear/authgear-server/pkg/lib/config" gomock "github.com/golang/mock/gomock" jsonpointer "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) @@ -281,33 +281,33 @@ func (mr *MockOAuthIdentityProviderMockRecorder) Get(userID, id interface{}) *go } // GetByProviderSubject mocks base method. -func (m *MockOAuthIdentityProvider) GetByProviderSubject(provider config.ProviderID, subjectID string) (*identity.OAuth, error) { +func (m *MockOAuthIdentityProvider) GetByProviderSubject(providerID oauthrelyingparty.ProviderID, subjectID string) (*identity.OAuth, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetByProviderSubject", provider, subjectID) + ret := m.ctrl.Call(m, "GetByProviderSubject", providerID, subjectID) ret0, _ := ret[0].(*identity.OAuth) ret1, _ := ret[1].(error) return ret0, ret1 } // GetByProviderSubject indicates an expected call of GetByProviderSubject. -func (mr *MockOAuthIdentityProviderMockRecorder) GetByProviderSubject(provider, subjectID interface{}) *gomock.Call { +func (mr *MockOAuthIdentityProviderMockRecorder) GetByProviderSubject(providerID, subjectID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByProviderSubject", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).GetByProviderSubject), provider, subjectID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByProviderSubject", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).GetByProviderSubject), providerID, subjectID) } // GetByUserProvider mocks base method. -func (m *MockOAuthIdentityProvider) GetByUserProvider(userID string, provider config.ProviderID) (*identity.OAuth, error) { +func (m *MockOAuthIdentityProvider) GetByUserProvider(userID string, providerID oauthrelyingparty.ProviderID) (*identity.OAuth, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetByUserProvider", userID, provider) + ret := m.ctrl.Call(m, "GetByUserProvider", userID, providerID) ret0, _ := ret[0].(*identity.OAuth) ret1, _ := ret[1].(error) return ret0, ret1 } // GetByUserProvider indicates an expected call of GetByUserProvider. -func (mr *MockOAuthIdentityProviderMockRecorder) GetByUserProvider(userID, provider interface{}) *gomock.Call { +func (mr *MockOAuthIdentityProviderMockRecorder) GetByUserProvider(userID, providerID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByUserProvider", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).GetByUserProvider), userID, provider) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByUserProvider", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).GetByUserProvider), userID, providerID) } // GetMany mocks base method. @@ -371,17 +371,17 @@ func (mr *MockOAuthIdentityProviderMockRecorder) ListByClaimJSONPointer(pointer, } // New mocks base method. -func (m *MockOAuthIdentityProvider) New(userID string, provider config.ProviderID, subjectID string, profile, claims map[string]interface{}) *identity.OAuth { +func (m *MockOAuthIdentityProvider) New(userID string, providerID oauthrelyingparty.ProviderID, subjectID string, profile, claims map[string]interface{}) *identity.OAuth { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "New", userID, provider, subjectID, profile, claims) + ret := m.ctrl.Call(m, "New", userID, providerID, subjectID, profile, claims) ret0, _ := ret[0].(*identity.OAuth) return ret0 } // New indicates an expected call of New. -func (mr *MockOAuthIdentityProviderMockRecorder) New(userID, provider, subjectID, profile, claims interface{}) *gomock.Call { +func (mr *MockOAuthIdentityProviderMockRecorder) New(userID, providerID, subjectID, profile, claims interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).New), userID, provider, subjectID, profile, claims) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockOAuthIdentityProvider)(nil).New), userID, providerID, subjectID, profile, claims) } // Update mocks base method. diff --git a/pkg/lib/authn/identity/service/service_test.go b/pkg/lib/authn/identity/service/service_test.go index a23f1fe332..876d2a444c 100644 --- a/pkg/lib/authn/identity/service/service_test.go +++ b/pkg/lib/authn/identity/service/service_test.go @@ -8,8 +8,10 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func newBool(b bool) *bool { @@ -53,11 +55,11 @@ func TestProviderListCandidates(t *testing.T) { Convey("oauth", func() { p.Authentication.Identities = []model.IdentityType{model.IdentityTypeOAuth} - p.Identity.OAuth.Providers = []config.OAuthSSOProviderConfig{ + p.Identity.OAuth.Providers = []oauthrelyingparty.ProviderConfig{ { - Alias: "google", - Type: "google", - ModifyDisabled: newBool(false), + "alias": "google", + "type": google.Type, + "modify_disabled": false, }, } @@ -103,11 +105,11 @@ func TestProviderListCandidates(t *testing.T) { }) Convey("respect authentication", func() { - p.Identity.OAuth.Providers = []config.OAuthSSOProviderConfig{ + p.Identity.OAuth.Providers = []oauthrelyingparty.ProviderConfig{ { - Alias: "google", - Type: "google", - ModifyDisabled: newBool(false), + "alias": "google", + "type": google.Type, + "modify_disabled": false, }, } p.Identity.LoginID.Keys = []config.LoginIDKeyConfig{ @@ -167,11 +169,11 @@ func TestProviderListCandidates(t *testing.T) { userID := "a" p.Authentication.Identities = []model.IdentityType{model.IdentityTypeOAuth} - p.Identity.OAuth.Providers = []config.OAuthSSOProviderConfig{ + p.Identity.OAuth.Providers = []oauthrelyingparty.ProviderConfig{ { - Alias: "google", - Type: "google", - ModifyDisabled: newBool(false), + "alias": "google", + "type": google.Type, + "modify_disabled": false, }, } @@ -179,8 +181,8 @@ func TestProviderListCandidates(t *testing.T) { siweProvider.EXPECT().List(userID).Return(nil, nil) oauthProvider.EXPECT().List(userID).Return([]*identity.OAuth{ { - ProviderID: config.ProviderID{ - Type: "google", + ProviderID: oauthrelyingparty.ProviderID{ + Type: google.Type, Keys: map[string]interface{}{}, }, ProviderSubjectID: "john.doe@gmail.com", diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 977afa512f..7a91f5ad68 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -3,30 +3,28 @@ package sso import ( "context" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/validation" ) type ADFSImpl struct { Clock clock.Clock - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } -func (*ADFSImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeADFS -} - -func (f *ADFSImpl) Config() config.OAuthSSOProviderConfig { +func (f *ADFSImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { - endpoint := f.ProviderConfig.DiscoveryDocumentEndpoint + endpoint := adfs.ProviderConfig(f.ProviderConfig).DiscoveryDocumentEndpoint() return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } @@ -36,9 +34,9 @@ func (f *ADFSImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return "", err } return c.MakeOAuthURL(AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -69,7 +67,7 @@ func (f *ADFSImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param f.Clock, r.Code, keySet, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, param.RedirectURI, param.Nonce, @@ -115,8 +113,9 @@ func (f *ADFSImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param } } + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() extracted, err = stdattrs.Extract(extracted, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/authn/sso/adfs_test.go index 9a2319843e..ff3b022205 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/authn/sso/adfs_test.go @@ -4,9 +4,11 @@ import ( "net/http" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" ) func TestADFSImpl(t *testing.T) { @@ -16,10 +18,10 @@ func TestADFSImpl(t *testing.T) { defer gock.Off() g := &ADFSImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeADFS, - DiscoveryDocumentEndpoint: "https://localhost/.well-known/openid-configuration", + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": adfs.Type, + "discovery_document_endpoint": "https://localhost/.well-known/openid-configuration", }, HTTPClient: client, } diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 34bf6d8d4e..74a60f51cd 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -8,8 +8,10 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/duration" @@ -24,13 +26,16 @@ var appleOIDCConfig = OIDCDiscoveryDocument{ type AppleImpl struct { Clock clock.Clock - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { + teamID := apple.ProviderConfig(f.ProviderConfig).TeamID() + keyID := apple.ProviderConfig(f.ProviderConfig).KeyID() + // https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens key, err := crypto.ParseAppleP8PrivateKey([]byte(f.Credentials.ClientSecret)) if err != nil { @@ -40,7 +45,7 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { now := f.Clock.NowUTC() payload := jwt.New() - _ = payload.Set(jwt.IssuerKey, f.ProviderConfig.TeamID) + _ = payload.Set(jwt.IssuerKey, teamID) _ = payload.Set(jwt.IssuedAtKey, now.Unix()) _ = payload.Set(jwt.ExpirationKey, now.Add(duration.Short).Unix()) _ = payload.Set(jwt.AudienceKey, "https://appleid.apple.com") @@ -50,7 +55,7 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { if err != nil { return } - _ = jwkKey.Set("kid", f.ProviderConfig.KeyID) + _ = jwkKey.Set("kid", keyID) token, err := jwtutil.Sign(payload, jwa.ES256, jwkKey) if err != nil { @@ -61,19 +66,15 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { return } -func (*AppleImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeApple -} - -func (f *AppleImpl) Config() config.OAuthSSOProviderConfig { +func (f *AppleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } func (f *AppleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return appleOIDCConfig.MakeOAuthURL(AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -103,7 +104,7 @@ func (f *AppleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param f.Clock, r.Code, keySet, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), clientSecret, param.RedirectURI, param.Nonce, @@ -148,8 +149,9 @@ func (f *AppleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = sub + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/authn/sso/apple_test.go index 49a9328176..876541092c 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/authn/sso/apple_test.go @@ -3,16 +3,18 @@ package sso import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" ) func TestAppleImpl(t *testing.T) { Convey("AppleImpl", t, func() { g := &AppleImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeApple, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": apple.Type, }, HTTPClient: OAuthHTTPClient{}, } diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 684cfd91fd..8d06183999 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -4,22 +4,25 @@ import ( "context" "fmt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" "github.com/authgear/authgear-server/pkg/util/clock" ) type Azureadb2cImpl struct { Clock clock.Clock - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { - tenant := f.ProviderConfig.Tenant - policy := f.ProviderConfig.Policy + azureadb2cConfig := azureadb2c.ProviderConfig(f.ProviderConfig) + tenant := azureadb2cConfig.Tenant() + policy := azureadb2cConfig.Policy() endpoint := fmt.Sprintf( "https://%s.b2clogin.com/%s.onmicrosoft.com/%s/v2.0/.well-known/openid-configuration", @@ -31,11 +34,7 @@ func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (f *Azureadb2cImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeAzureADB2C -} - -func (f *Azureadb2cImpl) Config() config.OAuthSSOProviderConfig { +func (f *Azureadb2cImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } @@ -45,9 +44,9 @@ func (f *Azureadb2cImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return "", err } return c.MakeOAuthURL(AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -77,7 +76,7 @@ func (f *Azureadb2cImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, f.Clock, r.Code, keySet, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, param.RedirectURI, param.Nonce, @@ -179,8 +178,9 @@ func (f *Azureadb2cImpl) Extract(claims map[string]interface{}) (stdattrs.T, err } out[stdattrs.Email] = email + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() return stdattrs.Extract(out, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) } diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/authn/sso/azureadb2c_test.go index 2b0c741cf9..8cc869d1fe 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/authn/sso/azureadb2c_test.go @@ -4,9 +4,11 @@ import ( "net/http" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" ) func TestAzureadb2cImpl(t *testing.T) { @@ -16,11 +18,11 @@ func TestAzureadb2cImpl(t *testing.T) { defer gock.Off() g := &Azureadb2cImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeAzureADB2C, - Tenant: "tenant", - Policy: "policy", + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": azureadb2c.Type, + "tenant": "tenant", + "policy": "policy", }, HTTPClient: client, } diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index ea9d522a7d..b571d12c16 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -4,14 +4,16 @@ import ( "context" "fmt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" "github.com/authgear/authgear-server/pkg/util/clock" ) type Azureadv2Impl struct { Clock clock.Clock - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient @@ -20,7 +22,8 @@ type Azureadv2Impl struct { func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { // OPTIMIZE(sso): Cache OpenID configuration - tenant := f.ProviderConfig.Tenant + tenant := azureadv2.ProviderConfig(f.ProviderConfig).Tenant() + var endpoint string // Azure special tenant // @@ -64,11 +67,7 @@ func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (*Azureadv2Impl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeAzureADv2 -} - -func (f *Azureadv2Impl) Config() config.OAuthSSOProviderConfig { +func (f *Azureadv2Impl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } @@ -78,9 +77,9 @@ func (f *Azureadv2Impl) GetAuthURL(param GetAuthURLParam) (string, error) { return "", err } return c.MakeOAuthURL(AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -110,7 +109,7 @@ func (f *Azureadv2Impl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p f.Clock, r.Code, keySet, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, param.RedirectURI, param.Nonce, @@ -136,8 +135,9 @@ func (f *Azureadv2Impl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = oid + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/authn/sso/azureadv2_test.go index 265494d699..ee5a6469b1 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/authn/sso/azureadv2_test.go @@ -4,9 +4,11 @@ import ( "net/http" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" ) func TestAzureadv2Impl(t *testing.T) { @@ -16,10 +18,10 @@ func TestAzureadv2Impl(t *testing.T) { defer gock.Off() g := &Azureadv2Impl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeAzureADv2, - Tenant: "common", + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": azureadv2.Type, + "tenant": "common", }, HTTPClient: client, } diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 2dfc9da550..1882acfb55 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -3,6 +3,7 @@ package sso import ( "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/crypto" @@ -16,25 +17,21 @@ const ( ) type FacebookImpl struct { - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } -func (*FacebookImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeFacebook -} - -func (f *FacebookImpl) Config() config.OAuthSSOProviderConfig { +func (f *FacebookImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } func (f *FacebookImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return MakeAuthorizationURL(facebookAuthorizationURL, AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, // ResponseMode is unset State: param.State, @@ -55,7 +52,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, r.Code, facebookTokenURL, param.RedirectURI, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, ) if err != nil { @@ -112,6 +109,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, } authInfo.ProviderUserID = id + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(map[string]interface{}{ stdattrs.Email: email, stdattrs.GivenName: firstName, @@ -120,7 +118,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, stdattrs.Nickname: shortName, stdattrs.Picture: picture, }, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/facebook_test.go b/pkg/lib/authn/sso/facebook_test.go index 9c3c48be8a..4743634f06 100644 --- a/pkg/lib/authn/sso/facebook_test.go +++ b/pkg/lib/authn/sso/facebook_test.go @@ -3,16 +3,18 @@ package sso import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" ) func TestFacebookImpl(t *testing.T) { Convey("FacebookImpl", t, func() { g := &FacebookImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeFacebook, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": facebook.Type, }, HTTPClient: OAuthHTTPClient{}, } diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index a212cf62c4..d2653e3665 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/authgear/authgear-server/pkg/api/apierrors" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/errorutil" @@ -21,26 +22,22 @@ const ( ) type GithubImpl struct { - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } -func (*GithubImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeGithub -} - -func (g *GithubImpl) Config() config.OAuthSSOProviderConfig { +func (g *GithubImpl) Config() oauthrelyingparty.ProviderConfig { return g.ProviderConfig } func (g *GithubImpl) GetAuthURL(param GetAuthURLParam) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return MakeAuthorizationURL(githubAuthorizationURL, AuthorizationURLParams{ - ClientID: g.ProviderConfig.ClientID, + ClientID: g.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: g.ProviderConfig.Type.Scope(), + Scope: g.ProviderConfig.Scope(), // ResponseType is unset. // ResponseMode is unset. State: param.State, @@ -74,6 +71,7 @@ func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p id := string(idJSONNumber) authInfo.ProviderUserID = id + emailRequired := g.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(map[string]interface{}{ stdattrs.Email: email, stdattrs.Name: login, @@ -81,7 +79,7 @@ func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p stdattrs.Picture: picture, stdattrs.Profile: profile, }, stdattrs.ExtractOptions{ - EmailRequired: *g.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { err = apierrors.AddDetails(err, errorutil.Details{ @@ -101,7 +99,7 @@ func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInfoParam) (accessTokenResp AccessTokenResp, err error) { q := make(url.Values) - q.Set("client_id", g.ProviderConfig.ClientID) + q.Set("client_id", g.ProviderConfig.ClientID()) q.Set("client_secret", g.Credentials.ClientSecret) q.Set("code", r.Code) q.Set("redirect_uri", param.RedirectURI) diff --git a/pkg/lib/authn/sso/github_test.go b/pkg/lib/authn/sso/github_test.go index 458e2f3489..b3fd638681 100644 --- a/pkg/lib/authn/sso/github_test.go +++ b/pkg/lib/authn/sso/github_test.go @@ -3,16 +3,18 @@ package sso import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" ) func TestGithubImpl(t *testing.T) { Convey("GithubImpl", t, func() { g := &GithubImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeGithub, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": github.Type, }, HTTPClient: OAuthHTTPClient{}, } diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index beaa7939e6..d751348309 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -3,6 +3,7 @@ package sso import ( "context" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" @@ -14,7 +15,7 @@ const ( type GoogleImpl struct { Clock clock.Clock - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient @@ -26,9 +27,9 @@ func (f *GoogleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return "", err } return d.MakeOAuthURL(AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -37,11 +38,7 @@ func (f *GoogleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { }), nil } -func (*GoogleImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeGoogle -} - -func (f *GoogleImpl) Config() config.OAuthSSOProviderConfig { +func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } @@ -66,7 +63,7 @@ func (f *GoogleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, para f.Clock, r.Code, keySet, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, param.RedirectURI, param.Nonce, @@ -105,8 +102,9 @@ func (f *GoogleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, para // Google supports // given_name, family_name, email, picture, profile, locale // https://developers.google.com/identity/protocols/oauth2/openid-connect#obtainuserinfo + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/authn/sso/google_test.go index 0c62c515de..2560702315 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/authn/sso/google_test.go @@ -4,9 +4,11 @@ import ( "net/http" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGoogleImpl(t *testing.T) { @@ -16,9 +18,9 @@ func TestGoogleImpl(t *testing.T) { defer gock.Off() g := &GoogleImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeGoogle, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": google.Type, }, HTTPClient: client, } diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index eaba263c5c..7834bd960f 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -1,6 +1,7 @@ package sso import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -14,25 +15,21 @@ const ( ) type LinkedInImpl struct { - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } -func (*LinkedInImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeLinkedIn -} - -func (f *LinkedInImpl) Config() config.OAuthSSOProviderConfig { +func (f *LinkedInImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } func (f *LinkedInImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return MakeAuthorizationURL(linkedinAuthorizationURL, AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID, + ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Type.Scope(), + Scope: f.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, // ResponseMode is unset. State: param.State, @@ -51,7 +48,7 @@ func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, r.Code, linkedinTokenURL, param.RedirectURI, - f.ProviderConfig.ClientID, + f.ProviderConfig.ClientID(), f.Credentials.ClientSecret, ) if err != nil { @@ -276,8 +273,9 @@ func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, id, attrs := decodeLinkedIn(combinedResponse) authInfo.ProviderUserID = id + emailRequired := f.ProviderConfig.EmailClaimConfig().Required() attrs, err = stdattrs.Extract(attrs, stdattrs.ExtractOptions{ - EmailRequired: *f.ProviderConfig.Claims.Email.Required, + EmailRequired: emailRequired, }) if err != nil { return diff --git a/pkg/lib/authn/sso/linkedin_test.go b/pkg/lib/authn/sso/linkedin_test.go index 572afefb13..d63c1dd104 100644 --- a/pkg/lib/authn/sso/linkedin_test.go +++ b/pkg/lib/authn/sso/linkedin_test.go @@ -3,16 +3,18 @@ package sso import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" ) func TestLinkedInImpl(t *testing.T) { Convey("LinkedInImpl", t, func() { g := &LinkedInImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeLinkedIn, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": linkedin.Type, }, HTTPClient: OAuthHTTPClient{}, } diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 3430f62e36..af4f923876 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -1,8 +1,18 @@ package sso import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/clock" ) @@ -25,8 +35,7 @@ type OAuthAuthorizationResponse struct { // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { - Type() config.OAuthSSOProviderType - Config() config.OAuthSSOProviderConfig + Config() oauthrelyingparty.ProviderConfig GetAuthURL(param GetAuthURLParam) (url string, err error) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) GetPrompt(prompt []string) []string @@ -75,8 +84,8 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return nil } - switch providerConfig.Type { - case config.OAuthSSOProviderTypeGoogle: + switch providerConfig.Type() { + case google.Type: return &GoogleImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, @@ -84,28 +93,28 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeFacebook: + case facebook.Type: return &FacebookImpl{ ProviderConfig: *providerConfig, Credentials: *credentials, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeGithub: + case github.Type: return &GithubImpl{ ProviderConfig: *providerConfig, Credentials: *credentials, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeLinkedIn: + case linkedin.Type: return &LinkedInImpl{ ProviderConfig: *providerConfig, Credentials: *credentials, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeAzureADv2: + case azureadv2.Type: return &Azureadv2Impl{ Clock: p.Clock, ProviderConfig: *providerConfig, @@ -113,7 +122,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeAzureADB2C: + case azureadb2c.Type: return &Azureadb2cImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, @@ -121,7 +130,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeADFS: + case adfs.Type: return &ADFSImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, @@ -129,7 +138,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeApple: + case apple.Type: return &AppleImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, @@ -137,7 +146,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } - case config.OAuthSSOProviderTypeWechat: + case wechat.Type: return &WechatImpl{ ProviderConfig: *providerConfig, Credentials: *credentials, diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index bb0ef2567c..9e0932b540 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -1,8 +1,10 @@ package sso import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) const ( @@ -10,26 +12,22 @@ const ( ) type WechatImpl struct { - ProviderConfig config.OAuthSSOProviderConfig + ProviderConfig oauthrelyingparty.ProviderConfig Credentials config.OAuthSSOProviderCredentialsItem StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } -func (*WechatImpl) Type() config.OAuthSSOProviderType { - return config.OAuthSSOProviderTypeWechat -} - -func (w *WechatImpl) Config() config.OAuthSSOProviderConfig { +func (w *WechatImpl) Config() oauthrelyingparty.ProviderConfig { return w.ProviderConfig } func (w *WechatImpl) GetAuthURL(param GetAuthURLParam) (string, error) { return MakeAuthorizationURL(wechatAuthorizationURL, AuthorizationURLParams{ // ClientID is not used by wechat. - WechatAppID: w.ProviderConfig.ClientID, + WechatAppID: w.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: w.ProviderConfig.Type.Scope(), + Scope: w.ProviderConfig.Scope(), ResponseType: ResponseTypeCode, // ResponseMode is unset. State: param.State, @@ -46,7 +44,7 @@ func (w *WechatImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, _ accessTokenResp, err := wechatFetchAccessTokenResp( w.HTTPClient, r.Code, - w.ProviderConfig.ClientID, + w.ProviderConfig.ClientID(), w.Credentials.ClientSecret, ) if err != nil { @@ -58,9 +56,9 @@ func (w *WechatImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, _ return } - config := w.Config() + is_sandbox_account := wechat.ProviderConfig(w.ProviderConfig).IsSandboxAccount() var userID string - if config.IsSandboxAccount { + if is_sandbox_account { if accessTokenResp.UnionID() != "" { err = InvalidConfiguration.New("invalid is_sandbox_account config, WeChat sandbox account should not have union id") return diff --git a/pkg/lib/authn/sso/wechat_test.go b/pkg/lib/authn/sso/wechat_test.go index fc0aa72cc0..5baa1d7335 100644 --- a/pkg/lib/authn/sso/wechat_test.go +++ b/pkg/lib/authn/sso/wechat_test.go @@ -3,17 +3,19 @@ package sso import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) func TestWechatImpl(t *testing.T) { Convey("WechatImpl", t, func() { g := &WechatImpl{ - ProviderConfig: config.OAuthSSOProviderConfig{ - ClientID: "client_id", - Type: config.OAuthSSOProviderTypeWechat, + ProviderConfig: oauthrelyingparty.ProviderConfig{ + "client_id": "client_id", + "type": wechat.Type, }, HTTPClient: OAuthHTTPClient{}, } diff --git a/pkg/lib/config/config.go b/pkg/lib/config/config.go index d409fae7cd..071b143db3 100644 --- a/pkg/lib/config/config.go +++ b/pkg/lib/config/config.go @@ -8,6 +8,7 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/api/model" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -101,7 +102,7 @@ func (c *AppConfig) Validate(ctx *validation.Context) { // Validation 1: lifetime of refresh token >= lifetime of access token c.validateTokenLifetime(ctx) - // Validation 2: OAuth provider cannot duplicate. + // Validation 2: oauth provider c.validateOAuthProvider(ctx) // Validation 3: identity must have usable primary authenticator. @@ -137,36 +138,26 @@ func (c *AppConfig) validateTokenLifetime(ctx *validation.Context) { } func (c *AppConfig) validateOAuthProvider(ctx *validation.Context) { - oAuthProviderIDs := map[string]struct{}{} + // We used to validate that ProviderID is unique. + // We now relax the validation, only alias is unique. oauthProviderAliases := map[string]struct{}{} - for i, provider := range c.Identity.OAuth.Providers { - // Ensure provider ID is not duplicated - // Except WeChat provider with different app type - providerID := map[string]interface{}{} - for k, v := range provider.ProviderID().Claims() { - providerID[k] = v - } - if provider.Type == OAuthSSOProviderTypeWechat { - providerID["app_type"] = provider.AppType - } - id, err := json.Marshal(providerID) - if err != nil { - panic("config: cannot marshal provider ID claims: " + err.Error()) - } - if _, ok := oAuthProviderIDs[string(id)]; ok { - ctx.Child("identity", "oauth", "providers", strconv.Itoa(i)). - EmitErrorMessage("duplicated OAuth provider") + for i, providerConfig := range c.Identity.OAuth.Providers { + // We used to ensure provider ID is not duplicated. + // We now expect alias to be unique. + alias := providerConfig.Alias() + childCtx := ctx.Child("identity", "oauth", "providers", strconv.Itoa(i)) + + if _, ok := oauthProviderAliases[alias]; ok { + childCtx.EmitErrorMessage("duplicated OAuth provider alias") continue } - oAuthProviderIDs[string(id)] = struct{}{} + oauthProviderAliases[alias] = struct{}{} - // Ensure alias is not duplicated. - if _, ok := oauthProviderAliases[provider.Alias]; ok { - ctx.Child("identity", "oauth", "providers", strconv.Itoa(i)). - EmitErrorMessage("duplicated OAuth provider alias") - continue + // Validate provider config if it is a builin provider. + provider := providerConfig.MustGetProvider() + if builtinProvider, ok := provider.(liboauthrelyingparty.BuiltinProvider); ok { + builtinProvider.ValidateProviderConfig(childCtx, providerConfig) } - oauthProviderAliases[provider.Alias] = struct{}{} } } diff --git a/pkg/lib/config/feature_identity.go b/pkg/lib/config/feature_identity.go index 7a20ea7539..1d73223834 100644 --- a/pkg/lib/config/feature_identity.go +++ b/pkg/lib/config/feature_identity.go @@ -1,5 +1,18 @@ package config +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" +) + var _ = FeatureConfigSchema.Add("IdentityFeatureConfig", ` { "type": "object", @@ -112,6 +125,32 @@ type OAuthSSOProvidersFeatureConfig struct { Wechat *OAuthSSOProviderFeatureConfig `json:"wechat,omitempty"` } +func (c *OAuthSSOProvidersFeatureConfig) IsDisabled(cfg oauthrelyingparty.ProviderConfig) bool { + switch cfg.Type() { + case google.Type: + return c.Google.Disabled + case facebook.Type: + return c.Facebook.Disabled + case github.Type: + return c.Github.Disabled + case linkedin.Type: + return c.LinkedIn.Disabled + case azureadv2.Type: + return c.Azureadv2.Disabled + case azureadb2c.Type: + return c.Azureadb2c.Disabled + case adfs.Type: + return c.ADFS.Disabled + case apple.Type: + return c.Apple.Disabled + case wechat.Type: + return c.Wechat.Disabled + default: + // Not a provider we recognize here. Allow it. + return false + } +} + var _ = FeatureConfigSchema.Add("OAuthSSOProviderFeatureConfig", ` { "type": "object", diff --git a/pkg/lib/config/identity.go b/pkg/lib/config/identity.go index bc7e92ddd1..1bf293fd51 100644 --- a/pkg/lib/config/identity.go +++ b/pkg/lib/config/identity.go @@ -1,10 +1,8 @@ package config import ( - "fmt" - "strconv" - "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" ) var _ = Schema.Add("IdentityConfig", ` @@ -250,18 +248,18 @@ var _ = Schema.Add("OAuthSSOConfig", ` "type": "object", "additionalProperties": false, "properties": { - "providers": { "type": "array", "items": { "$ref": "#/$defs/OAuthSSOProviderConfig" } } + "providers": { "type": "array", "items": { "type": "object" } } } } `) type OAuthSSOConfig struct { - Providers []OAuthSSOProviderConfig `json:"providers,omitempty"` + Providers []oauthrelyingparty.ProviderConfig `json:"providers,omitempty"` } -func (c *OAuthSSOConfig) GetProviderConfig(alias string) (*OAuthSSOProviderConfig, bool) { +func (c *OAuthSSOConfig) GetProviderConfig(alias string) (*oauthrelyingparty.ProviderConfig, bool) { for _, conf := range c.Providers { - if conf.Alias == alias { + if conf.Alias() == alias { cc := conf return &cc, true } @@ -269,317 +267,6 @@ func (c *OAuthSSOConfig) GetProviderConfig(alias string) (*OAuthSSOProviderConfi return nil, false } -var _ = Schema.Add("OAuthSSOProviderType", ` -{ - "type": "string", - "enum": [ - "google", - "facebook", - "github", - "linkedin", - "azureadv2", - "azureadb2c", - "adfs", - "apple", - "wechat" - ] -} -`) - -type OAuthSSOProviderType string - -func (t OAuthSSOProviderType) Scope() []string { - switch t { - case OAuthSSOProviderTypeGoogle: - // https://developers.google.com/identity/protocols/oauth2/openid-connect - return []string{"openid", "profile", "email"} - case OAuthSSOProviderTypeFacebook: - // https://developers.facebook.com/docs/permissions/reference - return []string{"email", "public_profile"} - case OAuthSSOProviderTypeGithub: - // https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps - return []string{"read:user", "user:email"} - case OAuthSSOProviderTypeLinkedIn: - // https://docs.microsoft.com/en-us/linkedin/shared/references/v2/profile/lite-profile - // https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/primary-contact-api?context=linkedin/compliance/context - return []string{"r_liteprofile", "r_emailaddress"} - case OAuthSSOProviderTypeAzureADv2: - // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes - return []string{"openid", "profile", "email"} - case OAuthSSOProviderTypeAzureADB2C: - // Instead of specifying scope to request a specific claim, - // the developer must customize the policy to allow which claims are returned to the relying party. - // If the developer is using User Flow policy, then those claims are called Application Claims. - return []string{"openid"} - case OAuthSSOProviderTypeADFS: - // The supported scopes are observed from a AD FS server. - return []string{"openid", "profile", "email"} - case OAuthSSOProviderTypeApple: - // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms - return []string{"name", "email"} - case OAuthSSOProviderTypeWechat: - // https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html - return []string{"snsapi_userinfo"} - } - - panic(fmt.Sprintf("oauth: unknown provider type %s", string(t))) -} - -func (t OAuthSSOProviderType) EmailRequired() bool { - switch t { - case OAuthSSOProviderTypeGoogle: - return true - case OAuthSSOProviderTypeFacebook: - return true - case OAuthSSOProviderTypeGithub: - return true - case OAuthSSOProviderTypeLinkedIn: - return true - case OAuthSSOProviderTypeAzureADv2: - return true - case OAuthSSOProviderTypeAzureADB2C: - return true - case OAuthSSOProviderTypeADFS: - return true - case OAuthSSOProviderTypeApple: - return true - case OAuthSSOProviderTypeWechat: - return false - } - panic(fmt.Sprintf("oauth: unknown provider type %s", string(t))) -} - -const ( - OAuthSSOProviderTypeGoogle OAuthSSOProviderType = "google" - OAuthSSOProviderTypeFacebook OAuthSSOProviderType = "facebook" - OAuthSSOProviderTypeGithub OAuthSSOProviderType = "github" - OAuthSSOProviderTypeLinkedIn OAuthSSOProviderType = "linkedin" - OAuthSSOProviderTypeAzureADv2 OAuthSSOProviderType = "azureadv2" - OAuthSSOProviderTypeAzureADB2C OAuthSSOProviderType = "azureadb2c" - OAuthSSOProviderTypeADFS OAuthSSOProviderType = "adfs" - OAuthSSOProviderTypeApple OAuthSSOProviderType = "apple" - OAuthSSOProviderTypeWechat OAuthSSOProviderType = "wechat" -) - -var OAuthSSOProviderTypes = []OAuthSSOProviderType{ - OAuthSSOProviderTypeGoogle, - OAuthSSOProviderTypeFacebook, - OAuthSSOProviderTypeGithub, - OAuthSSOProviderTypeLinkedIn, - OAuthSSOProviderTypeAzureADv2, - OAuthSSOProviderTypeAzureADB2C, - OAuthSSOProviderTypeADFS, - OAuthSSOProviderTypeApple, - OAuthSSOProviderTypeWechat, -} - -var _ = Schema.Add("OAuthSSOWeChatAppType", ` -{ - "type": "string", - "enum": [ - "mobile", - "web" - ] -} -`) - -type OAuthSSOWeChatAppType string - -const ( - OAuthSSOWeChatAppTypeWeb OAuthSSOWeChatAppType = "web" - OAuthSSOWeChatAppTypeMobile OAuthSSOWeChatAppType = "mobile" -) - -var _ = Schema.Add("OAuthSSOProviderConfig", ` -{ - "type": "object", - "additionalProperties": false, - "properties": { - "alias": { "type": "string" }, - "type": { "$ref": "#/$defs/OAuthSSOProviderType" }, - "modify_disabled": { "type": "boolean" }, - "client_id": { "type": "string", "minLength": 1 }, - "claims": { "$ref": "#/$defs/OAuthClaimsConfig" }, - "tenant": { "type": "string" }, - "policy": { "type": "string" }, - "key_id": { "type": "string" }, - "team_id": { "type": "string" }, - "app_type": { "$ref": "#/$defs/OAuthSSOWeChatAppType" }, - "account_id": { "type": "string", "format": "wechat_account_id"}, - "is_sandbox_account": { "type": "boolean" }, - "wechat_redirect_uris": { "type": "array", "items": { "type": "string", "format": "uri" } }, - "discovery_document_endpoint": { "type": "string", "format": "uri" } - }, - "required": ["alias", "type", "client_id"], - "allOf": [ - { - "if": { "properties": { "type": { "const": "apple" } } }, - "then": { - "required": ["key_id", "team_id"] - } - }, - { - "if": { "properties": { "type": { "const": "azureadv2" } } }, - "then": { - "required": ["tenant"] - } - }, - { - "if": { "properties": { "type": { "const": "wechat" } } }, - "then": { - "required": ["app_type", "account_id"] - } - }, - { - "if": { "properties": { "type": { "const": "adfs" } } }, - "then": { - "required": ["discovery_document_endpoint"] - } - }, - { - "if": { "properties": { "type": { "const": "azureadb2c" } } }, - "then": { - "required": ["tenant", "policy"] - } - } - ] -} -`) - -type OAuthSSOProviderConfig struct { - Alias string `json:"alias,omitempty"` - Type OAuthSSOProviderType `json:"type,omitempty"` - ModifyDisabled *bool `json:"modify_disabled,omitempty"` - ClientID string `json:"client_id"` - Claims *OAuthClaimsConfig `json:"claims,omitempty"` - - // Tenant is specific to `azureadv2` and `azureadb2c` - Tenant string `json:"tenant,omitempty"` - - // Policy is specific to `azureadb2c` - Policy string `json:"policy,omitempty"` - - // KeyID and TeamID are specific to `apple` - KeyID string `json:"key_id,omitempty"` - TeamID string `json:"team_id,omitempty"` - - // AppType is specific to `wechat`, support web or mobile - AppType OAuthSSOWeChatAppType `json:"app_type,omitempty"` - AccountID string `json:"account_id,omitempty"` - IsSandboxAccount bool `json:"is_sandbox_account,omitempty"` - WeChatRedirectURIs []string `json:"wechat_redirect_uris,omitempty"` - - // DiscoveryDocumentEndpoint is specific to `adfs`. - DiscoveryDocumentEndpoint string `json:"discovery_document_endpoint,omitempty"` -} - -func (c *OAuthSSOProviderConfig) SetDefaults() { - if c.ModifyDisabled == nil { - c.ModifyDisabled = newBool(false) - } - - if c.Claims.Email.Required == nil { - emailRequired := c.Type.EmailRequired() - c.Claims.Email.Required = &emailRequired - } -} - -func (c *OAuthSSOProviderConfig) ProviderID() ProviderID { - keys := map[string]interface{}{} - switch c.Type { - case OAuthSSOProviderTypeGoogle: - // Google supports OIDC. - // sub is public, not scoped to anything so changing client_id does not affect sub. - // Therefore, ProviderID is simply Type. - // - // Rotating the OAuth application is OK. - break - case OAuthSSOProviderTypeFacebook: - // Facebook does NOT support OIDC. - // Facebook user ID is scoped to client_id. - // Therefore, ProviderID is Type + client_id. - // - // Rotating the OAuth application is problematic. - // But if email remains unchanged, the user can associate their account. - keys["client_id"] = c.ClientID - case OAuthSSOProviderTypeGithub: - // Github does NOT support OIDC. - // Github user ID is public, not scoped to anything. - break - case OAuthSSOProviderTypeLinkedIn: - // LinkedIn is the same as Facebook. - keys["client_id"] = c.ClientID - case OAuthSSOProviderTypeAzureADv2: - // Azure AD v2 supports OIDC. - // sub is pairwise and is scoped to client_id. - // However, oid is powerful alternative to sub. - // oid is also pairwise and is scoped to tenant. - // We use oid as ProviderSubjectID so ProviderID is Type + tenant. - // - // Rotating the OAuth application is OK. - // But rotating the tenant is problematic. - // But if email remains unchanged, the user can associate their account. - keys["tenant"] = c.Tenant - case OAuthSSOProviderTypeAzureADB2C: - // By default sub is the Object ID of the user in the directory. - // A tenant is a directory. - // sub is scoped to the tenant only. - // Therefore, ProviderID is Type + tenant. - // - // See https://docs.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview#claims - keys["tenant"] = c.Tenant - case OAuthSSOProviderTypeApple: - // Apple supports OIDC. - // sub is pairwise and is scoped to team_id. - // Therefore, ProviderID is Type + team_id. - // - // Rotating the OAuth application is OK. - // But rotating the Apple Developer account is problematic. - // Since Apple has private relay to hide the real email, - // the user may not be associate their account. - keys["team_id"] = c.TeamID - case OAuthSSOProviderTypeWechat: - // WeChat does NOT support OIDC. - // In the same Weixin Open Platform account, the user UnionID is unique. - // The id is scoped to Open Platform account. - // https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html - keys["account_id"] = c.AccountID - keys["is_sandbox_account"] = strconv.FormatBool(c.IsSandboxAccount) - } - - return ProviderID{ - Type: string(c.Type), - Keys: keys, - } -} - -// ProviderID combining with a subject ID identifies an user from an external system. -type ProviderID struct { - Type string - Keys map[string]interface{} -} - -func (p ProviderID) Claims() map[string]interface{} { - claim := map[string]interface{}{} - claim["type"] = p.Type - for k, v := range p.Keys { - claim[k] = v - } - return claim -} - -func (p ProviderID) Equal(that *ProviderID) bool { - if p.Type != that.Type || len(p.Keys) != len(that.Keys) { - return false - } - for k, v := range p.Keys { - if tv, ok := that.Keys[k]; !ok || tv != v { - return false - } - } - return true -} - var _ = Schema.Add("PromotionConflictBehavior", ` { "type": "string", @@ -633,40 +320,3 @@ func (c *BiometricConfig) SetDefaults() { c.ListEnabled = newBool(false) } } - -var _ = Schema.Add("OAuthClaimsConfig", ` -{ - "type": "object", - "additionalProperties": false, - "properties": { - "email": { "$ref": "#/$defs/OAuthClaimConfig" } - } -} -`) - -type OAuthClaimsConfig struct { - Email *OAuthClaimConfig `json:"email,omitempty"` -} - -var _ = Schema.Add("OAuthClaimConfig", ` -{ - "type": "object", - "additionalProperties": false, - "properties": { - "assume_verified": { "type": "boolean" }, - "required": { "type": "boolean" } - } -} -`) - -type OAuthClaimConfig struct { - AssumeVerified *bool `json:"assume_verified,omitempty"` - Required *bool `json:"required,omitempty"` -} - -func (c *OAuthClaimConfig) SetDefaults() { - if c.AssumeVerified == nil { - c.AssumeVerified = newBool(true) - } - // Required is type-specific so the default is not set here. -} diff --git a/pkg/lib/config/secret.go b/pkg/lib/config/secret.go index b737ab6cda..66921d8697 100644 --- a/pkg/lib/config/secret.go +++ b/pkg/lib/config/secret.go @@ -149,18 +149,19 @@ func (c *SecretConfig) validateOAuthProviders(ctx *validation.Context, appConfig oauth, ok := data.(*OAuthSSOProviderCredentials) if ok { for _, p := range appConfig.Identity.OAuth.Providers { + providerAlias := p.Alias() var matchedItem *OAuthSSOProviderCredentialsItem = nil var matchedItemIndex int = -1 for index := range oauth.Items { item := oauth.Items[index] - if p.Alias == item.Alias { + if providerAlias == item.Alias { matchedItem = &item matchedItemIndex = index break } } if matchedItem == nil { - ctx.EmitErrorMessage(fmt.Sprintf("OAuth SSO provider client credentials for '%s' is required", p.Alias)) + ctx.EmitErrorMessage(fmt.Sprintf("OAuth SSO provider client credentials for '%s' is required", providerAlias)) } else { if matchedItem.ClientSecret == "" { ctx.Child("secrets", fmt.Sprintf("%d", secretIndex), "data", "items", fmt.Sprintf("%d", matchedItemIndex)).EmitError( diff --git a/pkg/lib/config/testdata/config_tests.yaml b/pkg/lib/config/testdata/config_tests.yaml index c8676cc640..02c1b2824d 100644 --- a/pkg/lib/config/testdata/config_tests.yaml +++ b/pkg/lib/config/testdata/config_tests.yaml @@ -209,68 +209,6 @@ config: x_application_type: confidential redirect_uris: - "http://example.com/oauth-redirect" ---- -name: dupe-oauth-provider -error: |- - invalid configuration: - /identity/oauth/providers/1: duplicated OAuth provider -config: - id: test - http: - public_origin: http://test - identity: - oauth: - providers: - - alias: google_a - type: google - client_id: client_a - - alias: google_b - type: google - client_id: client_b - ---- -name: dupe-wechat-oauth-provider -error: |- - invalid configuration: - /identity/oauth/providers/1: duplicated OAuth provider -config: - id: test - http: - public_origin: http://test - identity: - oauth: - providers: - - alias: wechat_a - type: wechat - client_id: client_id_a - account_id: gh_accountid - app_type: mobile - - alias: wechat_b - type: wechat - client_id: client_id_b - account_id: gh_accountid - app_type: mobile - ---- -name: dupe-wechat-oauth-provider-different-app-type -error: null -config: - id: test - http: - public_origin: http://test - identity: - oauth: - providers: - - alias: wechat_a - type: wechat - client_id: client_id_a - account_id: gh_accountida - app_type: mobile - - alias: wechat_b - type: wechat - client_id: client_id_b - account_id: gh_accountidb - app_type: web --- name: invalid-wechat-oauth-provider-account-id @@ -296,7 +234,7 @@ name: missing-oauth-provider-alias error: |- invalid configuration: /identity/oauth/providers/0: required - map[actual:[client_id type] expected:[alias client_id type] missing:[alias]] + map[actual:[claims client_id modify_disabled type] expected:[alias client_id type] missing:[alias]] config: id: test http: @@ -331,7 +269,7 @@ name: oauth-provider-apple error: |- invalid configuration: /identity/oauth/providers/0: required - map[actual:[alias client_id type] expected:[key_id team_id] missing:[key_id team_id]] + map[actual:[alias claims client_id modify_disabled type] expected:[alias client_id key_id team_id type] missing:[key_id team_id]] config: id: test http: @@ -348,7 +286,7 @@ name: oauth-provider-azureadv2 error: |- invalid configuration: /identity/oauth/providers/0: required - map[actual:[alias client_id type] expected:[tenant] missing:[tenant]] + map[actual:[alias claims client_id modify_disabled type] expected:[alias client_id tenant type] missing:[tenant]] config: id: test http: @@ -364,7 +302,7 @@ name: oauth-provider-azureadb2c error: |- invalid configuration: /identity/oauth/providers/0: required - map[actual:[alias client_id type] expected:[policy tenant] missing:[policy tenant]] + map[actual:[alias claims client_id modify_disabled type] expected:[alias client_id policy tenant type] missing:[policy tenant]] config: id: test http: @@ -381,7 +319,7 @@ name: oauth-provider-adfs error: |- invalid configuration: /identity/oauth/providers/0: required - map[actual:[alias client_id type] expected:[discovery_document_endpoint] missing:[discovery_document_endpoint]] + map[actual:[alias claims client_id modify_disabled type] expected:[alias client_id discovery_document_endpoint type] missing:[discovery_document_endpoint]] config: id: test http: diff --git a/pkg/lib/facade/coordinator.go b/pkg/lib/facade/coordinator.go index 99a130d404..4930baa321 100644 --- a/pkg/lib/facade/coordinator.go +++ b/pkg/lib/facade/coordinator.go @@ -9,6 +9,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/event/blocking" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator/service" @@ -588,11 +589,11 @@ func (c *Coordinator) markOAuthEmailAsVerified(info *identity.Info) error { providerID := info.OAuth.ProviderID - var cfg *config.OAuthSSOProviderConfig + var cfg oauthrelyingparty.ProviderConfig for _, c := range c.IdentityConfig.OAuth.Providers { - if c.ProviderID().Equal(&providerID) { - c := c - cfg = &c + c := c + if c.ProviderID().Equal(providerID) { + cfg = c break } } @@ -600,13 +601,16 @@ func (c *Coordinator) markOAuthEmailAsVerified(info *identity.Info) error { standardClaims := info.IdentityAwareStandardClaims() email, ok := standardClaims[model.ClaimEmail] - if ok && cfg != nil && *cfg.Claims.Email.AssumeVerified { - // Mark as verified if OAuth email is assumed to be verified - err := c.markVerified(info.UserID, map[model.ClaimName]string{ - model.ClaimEmail: email, - }) - if err != nil { - return err + if ok && cfg != nil { + assumedVerified := cfg.EmailClaimConfig().AssumeVerified() + if assumedVerified { + // Mark as verified if OAuth email is assumed to be verified + err := c.markVerified(info.UserID, map[model.ClaimName]string{ + model.ClaimEmail: email, + }) + if err != nil { + return err + } } } diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 842892a00b..638a946004 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -4,10 +4,12 @@ import ( "net/url" "github.com/authgear/authgear-server/pkg/api" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/interaction" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/crypto" ) @@ -24,7 +26,7 @@ type InputUseIdentityOAuthProvider interface { type EdgeUseIdentityOAuthProvider struct { IsAuthentication bool IsCreating bool - Configs []config.OAuthSSOProviderConfig + Configs []oauthrelyingparty.ProviderConfig FeatureConfig *config.OAuthSSOProvidersFeatureConfig } @@ -32,8 +34,8 @@ func (e *EdgeUseIdentityOAuthProvider) GetIdentityCandidates() []identity.Candid candidates := []identity.Candidate{} for _, c := range e.Configs { conf := c - if !identity.IsOAuthSSOProviderTypeDisabled(conf.Type, e.FeatureConfig) { - candidates = append(candidates, identity.NewOAuthCandidate(&conf)) + if !identity.IsOAuthSSOProviderTypeDisabled(conf, e.FeatureConfig) { + candidates = append(candidates, identity.NewOAuthCandidate(conf)) } } return candidates @@ -46,14 +48,14 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra } alias := input.GetProviderAlias() - var oauthConfig *config.OAuthSSOProviderConfig + var oauthConfig oauthrelyingparty.ProviderConfig for _, c := range e.Configs { - if identity.IsOAuthSSOProviderTypeDisabled(c.Type, e.FeatureConfig) { + if identity.IsOAuthSSOProviderTypeDisabled(c, e.FeatureConfig) { continue } - if c.Alias == alias { + if c.Alias() == alias { conf := c - oauthConfig = &conf + oauthConfig = conf break } } @@ -73,7 +75,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra redirectURIForOAuthProvider := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias).String() // Special case: wechat needs to use a special callback endpoint. - if oauthProvider.Config().Type == config.OAuthSSOProviderTypeWechat { + if oauthProvider.Config().Type() == wechat.Type { redirectURIForOAuthProvider = ctx.OAuthRedirectURIBuilder.WeChatCallbackEndpointURL().String() } @@ -91,7 +93,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra } // Special case: wechat needs to redirect a special page. - if oauthProvider.Config().Type == config.OAuthSSOProviderTypeWechat { + if oauthProvider.Config().Type() == wechat.Type { v := url.Values{} v.Add("x_auth_url", redirectURI) redirectURI = ctx.OAuthRedirectURIBuilder.WeChatAuthorizeURL(alias).String() + "?" + v.Encode() @@ -100,7 +102,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra return &NodeUseIdentityOAuthProvider{ IsAuthentication: e.IsAuthentication, IsCreating: e.IsCreating, - Config: *oauthConfig, + Config: oauthConfig, HashedNonce: nonce, ErrorRedirectURI: errorRedirectURI, RedirectURI: redirectURI, @@ -108,12 +110,12 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra } type NodeUseIdentityOAuthProvider struct { - IsAuthentication bool `json:"is_authentication"` - IsCreating bool `json:"is_creating"` - Config config.OAuthSSOProviderConfig `json:"provider_config"` - HashedNonce string `json:"hashed_nonce"` - ErrorRedirectURI string `json:"error_redirect_uri"` - RedirectURI string `json:"redirect_uri"` + IsAuthentication bool `json:"is_authentication"` + IsCreating bool `json:"is_creating"` + Config oauthrelyingparty.ProviderConfig `json:"provider_config"` + HashedNonce string `json:"hashed_nonce"` + ErrorRedirectURI string `json:"error_redirect_uri"` + RedirectURI string `json:"redirect_uri"` } // GetRedirectURI implements RedirectURIGetter. diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 208ce3ef9d..711ca7168d 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -6,9 +6,9 @@ import ( "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/sso" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/util/crypto" ) @@ -28,7 +28,7 @@ type InputUseIdentityOAuthUserInfo interface { type EdgeUseIdentityOAuthUserInfo struct { IsAuthentication bool IsCreating bool - Config config.OAuthSSOProviderConfig + Config oauthrelyingparty.ProviderConfig HashedNonce string ErrorRedirectURI string } @@ -47,8 +47,9 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra errorURI := input.GetErrorURI() hashedNonce := e.HashedNonce - if e.Config.Alias != alias { - return nil, fmt.Errorf("interaction: unexpected provider alias %s != %s", e.Config.Alias, alias) + providerConfigAlias := e.Config.Alias() + if providerConfigAlias != alias { + return nil, fmt.Errorf("interaction: unexpected provider alias %s != %s", providerConfigAlias, alias) } oauthProvider := ctx.OAuthProviderFactory.NewOAuthProvider(alias) diff --git a/pkg/lib/oauthrelyingparty/adfs/provider.go b/pkg/lib/oauthrelyingparty/adfs/provider.go new file mode 100644 index 0000000000..5f68ca9728 --- /dev/null +++ b/pkg/lib/oauthrelyingparty/adfs/provider.go @@ -0,0 +1,74 @@ +package adfs + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, ADFS{}) +} + +const Type = liboauthrelyingparty.TypeADFS + +type ProviderConfig oauthrelyingparty.ProviderConfig + +func (c ProviderConfig) DiscoveryDocumentEndpoint() string { + discovery_document_endpoint, _ := c["discovery_document_endpoint"].(string) + return discovery_document_endpoint +} + +var _ oauthrelyingparty.Provider = ADFS{} +var _ liboauthrelyingparty.BuiltinProvider = ADFS{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + }, + "discovery_document_endpoint": { "type": "string", "format": "uri" } + }, + "required": ["alias", "type", "client_id", "discovery_document_endpoint"] +} +`) + +type ADFS struct{} + +func (ADFS) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (ADFS) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (ADFS) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // In the original implementation, provider ID is just type. + return oauthrelyingparty.NewProviderID(cfg.Type(), nil) +} + +func (ADFS) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // The supported scopes are observed from a AD FS server. + return []string{"openid", "profile", "email"} +} diff --git a/pkg/lib/oauthrelyingparty/apple/provider.go b/pkg/lib/oauthrelyingparty/apple/provider.go new file mode 100644 index 0000000000..94f55db4ed --- /dev/null +++ b/pkg/lib/oauthrelyingparty/apple/provider.go @@ -0,0 +1,91 @@ +package apple + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Apple{}) +} + +const Type = liboauthrelyingparty.TypeApple + +type ProviderConfig oauthrelyingparty.ProviderConfig + +func (c ProviderConfig) TeamID() string { + team_id, _ := c["team_id"].(string) + return team_id +} + +func (c ProviderConfig) KeyID() string { + key_id, _ := c["key_id"].(string) + return key_id +} + +var _ oauthrelyingparty.Provider = Apple{} +var _ liboauthrelyingparty.BuiltinProvider = Apple{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + }, + "key_id": { "type": "string" }, + "team_id": { "type": "string" } + }, + "required": ["alias", "type", "client_id", "key_id", "team_id"] +} +`) + +type Apple struct{} + +func (Apple) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Apple) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (Apple) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + team_id := ProviderConfig(cfg).TeamID() + // Apple supports OIDC. + // sub is pairwise and is scoped to team_id. + // Therefore, ProviderID is Type + team_id. + // + // Rotating the OAuth application is OK. + // But rotating the Apple Developer account is problematic. + // Since Apple has private relay to hide the real email, + // the user may not be associate their account. + keys := map[string]interface{}{ + "team_id": team_id, + } + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (Apple) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms + return []string{"name", "email"} +} diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go new file mode 100644 index 0000000000..bf38e3fb6c --- /dev/null +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go @@ -0,0 +1,91 @@ +package azureadb2c + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, AzureADB2C{}) +} + +const Type = liboauthrelyingparty.TypeAzureADB2C + +type ProviderConfig oauthrelyingparty.ProviderConfig + +func (c ProviderConfig) Tenant() string { + tenant, _ := c["tenant"].(string) + return tenant +} + +func (c ProviderConfig) Policy() string { + policy, _ := c["policy"].(string) + return policy +} + +var _ oauthrelyingparty.Provider = AzureADB2C{} +var _ liboauthrelyingparty.BuiltinProvider = AzureADB2C{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + }, + "tenant": { "type": "string" }, + "policy": { "type": "string" } + }, + "required": ["alias", "type", "client_id", "tenant", "policy"] +} +`) + +type AzureADB2C struct{} + +func (AzureADB2C) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (AzureADB2C) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (AzureADB2C) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // By default sub is the Object ID of the user in the directory. + // A tenant is a directory. + // sub is scoped to the tenant only. + // Therefore, ProviderID is Type + tenant. + // + // See https://docs.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview#claims + tenant := ProviderConfig(cfg).Tenant() + keys := map[string]interface{}{ + "tenant": tenant, + } + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (AzureADB2C) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // Instead of specifying scope to request a specific claim, + // the developer must customize the policy to allow which claims are returned to the relying party. + // If the developer is using User Flow policy, then those claims are called Application Claims. + return []string{"openid"} +} diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider.go b/pkg/lib/oauthrelyingparty/azureadv2/provider.go new file mode 100644 index 0000000000..31eb995ab7 --- /dev/null +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider.go @@ -0,0 +1,86 @@ +package azureadv2 + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, AzureADv2{}) +} + +const Type = liboauthrelyingparty.TypeAzureADv2 + +type ProviderConfig oauthrelyingparty.ProviderConfig + +func (c ProviderConfig) Tenant() string { + tenant, _ := c["tenant"].(string) + return tenant +} + +var _ oauthrelyingparty.Provider = AzureADv2{} +var _ liboauthrelyingparty.BuiltinProvider = AzureADv2{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + }, + "tenant": { "type": "string" } + }, + "required": ["alias", "type", "client_id", "tenant"] +} +`) + +type AzureADv2 struct{} + +func (AzureADv2) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (AzureADv2) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (AzureADv2) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // Azure AD v2 supports OIDC. + // sub is pairwise and is scoped to client_id. + // However, oid is powerful alternative to sub. + // oid is also pairwise and is scoped to tenant. + // We use oid as ProviderSubjectID so ProviderID is Type + tenant. + // + // Rotating the OAuth application is OK. + // But rotating the tenant is problematic. + // But if email remains unchanged, the user can associate their account. + tenant := ProviderConfig(cfg).Tenant() + keys := map[string]interface{}{ + "tenant": tenant, + } + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (AzureADv2) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes + return []string{"openid", "profile", "email"} +} diff --git a/pkg/lib/oauthrelyingparty/builtin.go b/pkg/lib/oauthrelyingparty/builtin.go new file mode 100644 index 0000000000..8da1e3c7de --- /dev/null +++ b/pkg/lib/oauthrelyingparty/builtin.go @@ -0,0 +1,34 @@ +package oauthrelyingparty + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +const ( + TypeGoogle = "google" + TypeFacebook = "facebook" + TypeGithub = "github" + TypeLinkedin = "linkedin" + TypeAzureADv2 = "azureadv2" + TypeAzureADB2C = "azureadb2c" + TypeADFS = "adfs" + TypeApple = "apple" + TypeWechat = "wechat" +) + +var BuiltinProviderTypes = []string{ + TypeGoogle, + TypeFacebook, + TypeGithub, + TypeLinkedin, + TypeAzureADv2, + TypeAzureADB2C, + TypeADFS, + TypeApple, + TypeWechat, +} + +type BuiltinProvider interface { + ValidateProviderConfig(ctx *validation.Context, providerConfig oauthrelyingparty.ProviderConfig) +} diff --git a/pkg/lib/oauthrelyingparty/facebook/provider.go b/pkg/lib/oauthrelyingparty/facebook/provider.go new file mode 100644 index 0000000000..e702ffec5c --- /dev/null +++ b/pkg/lib/oauthrelyingparty/facebook/provider.go @@ -0,0 +1,74 @@ +package facebook + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Facebook{}) +} + +const Type = liboauthrelyingparty.TypeFacebook + +var _ oauthrelyingparty.Provider = Facebook{} +var _ liboauthrelyingparty.BuiltinProvider = Facebook{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + } + }, + "required": ["alias", "type", "client_id"] +} +`) + +type Facebook struct{} + +func (Facebook) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Facebook) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (Facebook) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // Facebook does NOT support OIDC. + // Facebook user ID is scoped to client_id. + // Therefore, ProviderID is Type + client_id. + // + // Rotating the OAuth application is problematic. + // But if email remains unchanged, the user can associate their account. + keys := map[string]interface{}{ + "client_id": cfg.ClientID(), + } + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (Facebook) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://developers.facebook.com/docs/permissions/reference + return []string{"email", "public_profile"} +} diff --git a/pkg/lib/oauthrelyingparty/github/provider.go b/pkg/lib/oauthrelyingparty/github/provider.go new file mode 100644 index 0000000000..a6960a9783 --- /dev/null +++ b/pkg/lib/oauthrelyingparty/github/provider.go @@ -0,0 +1,67 @@ +package github + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Github{}) +} + +const Type = liboauthrelyingparty.TypeGithub + +var _ oauthrelyingparty.Provider = Github{} +var _ liboauthrelyingparty.BuiltinProvider = Github{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + } + }, + "required": ["alias", "type", "client_id"] +} +`) + +type Github struct{} + +func (Github) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Github) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (Github) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // Github does NOT support OIDC. + // Github user ID is public, not scoped to anything. + return oauthrelyingparty.NewProviderID(cfg.Type(), nil) +} + +func (Github) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps + return []string{"read:user", "user:email"} +} diff --git a/pkg/lib/oauthrelyingparty/google/provider.go b/pkg/lib/oauthrelyingparty/google/provider.go new file mode 100644 index 0000000000..38c805b91a --- /dev/null +++ b/pkg/lib/oauthrelyingparty/google/provider.go @@ -0,0 +1,70 @@ +package google + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Google{}) +} + +const Type = liboauthrelyingparty.TypeGoogle + +var _ oauthrelyingparty.Provider = Google{} +var _ liboauthrelyingparty.BuiltinProvider = Google{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + } + }, + "required": ["alias", "type", "client_id"] +} +`) + +type Google struct{} + +func (Google) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Google) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (Google) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // Google supports OIDC. + // sub is public, not scoped to anything so changing client_id does not affect sub. + // Therefore, ProviderID is simply the type. + // + // Rotating the OAuth application is OK. + return oauthrelyingparty.NewProviderID(cfg.Type(), nil) +} + +func (Google) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://developers.google.com/identity/protocols/oauth2/openid-connect + return []string{"openid", "profile", "email"} +} diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider.go b/pkg/lib/oauthrelyingparty/linkedin/provider.go new file mode 100644 index 0000000000..c1591fb4d1 --- /dev/null +++ b/pkg/lib/oauthrelyingparty/linkedin/provider.go @@ -0,0 +1,74 @@ +package linkedin + +import ( + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Linkedin{}) +} + +const Type = liboauthrelyingparty.TypeLinkedin + +var _ oauthrelyingparty.Provider = Linkedin{} +var _ liboauthrelyingparty.BuiltinProvider = Linkedin{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + } + }, + "required": ["alias", "type", "client_id"] +} +`) + +type Linkedin struct{} + +func (Linkedin) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Linkedin) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_Required()) +} + +func (Linkedin) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // Linkedin does NOT support OIDC. + // Linkedin user ID is scoped to client_id. + // Therefore, ProviderID is Type + client_id. + // + // Rotating the OAuth application is problematic. + keys := map[string]interface{}{ + "client_id": cfg.ClientID(), + } + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (Linkedin) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://docs.microsoft.com/en-us/linkedin/shared/references/v2/profile/lite-profile + // https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/primary-contact-api?context=linkedin/compliance/context + return []string{"r_liteprofile", "r_emailaddress"} +} diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go new file mode 100644 index 0000000000..b28c7b5c87 --- /dev/null +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go @@ -0,0 +1,17 @@ +package oauthrelyingpartyutil + +import "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + +func Email_AssumeVerified_Required() oauthrelyingparty.ProviderClaimConfig { + return oauthrelyingparty.ProviderClaimConfig{ + "assume_verified": true, + "required": true, + } +} + +func Email_AssumeVerified_NOT_Required() oauthrelyingparty.ProviderClaimConfig { + return oauthrelyingparty.ProviderClaimConfig{ + "assume_verified": true, + "required": false, + } +} diff --git a/pkg/lib/oauthrelyingparty/wechat/provider.go b/pkg/lib/oauthrelyingparty/wechat/provider.go new file mode 100644 index 0000000000..1b2b6bce5f --- /dev/null +++ b/pkg/lib/oauthrelyingparty/wechat/provider.go @@ -0,0 +1,119 @@ +package wechat + +import ( + "strconv" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/validation" +) + +func init() { + oauthrelyingparty.RegisterProvider(Type, Wechat{}) +} + +type AppType string + +const ( + AppTypeWeb AppType = "web" + AppTypeMobile AppType = "mobile" +) + +type ProviderConfig oauthrelyingparty.ProviderConfig + +func (c ProviderConfig) AppType() AppType { + app_type, _ := c["app_type"].(string) + return AppType(app_type) +} + +func (c ProviderConfig) AccountID() string { + account_id, _ := c["account_id"].(string) + return account_id +} + +func (c ProviderConfig) IsSandboxAccount() bool { + is_sandbox_account, _ := c["is_sandbox_account"].(bool) + return is_sandbox_account +} + +func (c ProviderConfig) WechatRedirectURIs() []string { + var out []string + wechat_redirect_uris, _ := c["wechat_redirect_uris"].([]interface{}) + for _, iface := range wechat_redirect_uris { + if s, ok := iface.(string); ok { + out = append(out, s) + } + } + return out +} + +const Type = liboauthrelyingparty.TypeWechat + +var _ oauthrelyingparty.Provider = Wechat{} +var _ liboauthrelyingparty.BuiltinProvider = Wechat{} + +var Schema = validation.NewSimpleSchema(` +{ + "type": "object", + "additionalProperties": false, + "properties": { + "alias": { "type": "string" }, + "type": { "type": "string" }, + "modify_disabled": { "type": "boolean" }, + "client_id": { "type": "string", "minLength": 1 }, + "claims": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "type": "object", + "additionalProperties": false, + "properties": { + "assume_verified": { "type": "boolean" }, + "required": { "type": "boolean" } + } + } + } + }, + "app_type": { "type": "string", "enum": ["mobile", "web"] }, + "account_id": { "type": "string", "format": "wechat_account_id" }, + "is_sandbox_account": { "type": "boolean" }, + "wechat_redirect_uris": { "type": "array", "items": { "type": "string", "format": "uri" } } + }, + "required": ["alias", "type", "client_id", "app_type", "account_id"] +} +`) + +type Wechat struct{} + +func (Wechat) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { + ctx.AddError(Schema.Validator().ValidateValue(cfg)) +} + +func (Wechat) SetDefaults(cfg oauthrelyingparty.ProviderConfig) { + cfg.SetDefaultsModifyDisabledFalse() + cfg.SetDefaultsEmailClaimConfig(oauthrelyingpartyutil.Email_AssumeVerified_NOT_Required()) +} + +func (Wechat) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.ProviderID { + // WeChat does NOT support OIDC. + // In the same Weixin Open Platform account, the user UnionID is unique. + // The id is scoped to Open Platform account. + // https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html + + wechatCfg := ProviderConfig(cfg) + account_id := wechatCfg.AccountID() + is_sandbox_account := wechatCfg.IsSandboxAccount() + keys := map[string]interface{}{ + "account_id": account_id, + "is_sandbox_account": strconv.FormatBool(is_sandbox_account), + } + + return oauthrelyingparty.NewProviderID(cfg.Type(), keys) +} + +func (Wechat) Scope(_ oauthrelyingparty.ProviderConfig) []string { + // https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html + return []string{"snsapi_userinfo"} +} From d77904635b3a09260290b671983f00e6645a0020 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 9 May 2024 19:08:43 +0800 Subject: [PATCH 02/30] Move access_token.go to oauthrelyingpartyutil --- pkg/api/oauthrelyingparty/error.go | 7 +++++ .../declarative/utils_common.go | 3 ++- pkg/lib/authn/sso/adfs.go | 3 ++- pkg/lib/authn/sso/apple.go | 3 ++- pkg/lib/authn/sso/azureadb2c.go | 3 ++- pkg/lib/authn/sso/azureadv2.go | 3 ++- pkg/lib/authn/sso/error.go | 26 ------------------- pkg/lib/authn/sso/facebook.go | 5 ++-- pkg/lib/authn/sso/github.go | 9 ++++--- pkg/lib/authn/sso/google.go | 3 ++- pkg/lib/authn/sso/linkedin.go | 5 ++-- pkg/lib/authn/sso/oidc.go | 8 +++--- pkg/lib/authn/sso/user_profile.go | 4 ++- .../nodes/use_identity_oauth_user_info.go | 3 ++- .../oauthrelyingpartyutil}/access_token.go | 13 ++++++---- .../oauthrelyingpartyutil/error.go | 25 ++++++++++++++++++ 16 files changed, 73 insertions(+), 50 deletions(-) create mode 100644 pkg/api/oauthrelyingparty/error.go rename pkg/lib/{authn/sso => oauthrelyingparty/oauthrelyingpartyutil}/access_token.go (89%) create mode 100644 pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go diff --git a/pkg/api/oauthrelyingparty/error.go b/pkg/api/oauthrelyingparty/error.go new file mode 100644 index 0000000000..4007e33470 --- /dev/null +++ b/pkg/api/oauthrelyingparty/error.go @@ -0,0 +1,7 @@ +package oauthrelyingparty + +type ErrorResponse struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description,omitempty"` + ErrorURI string `json:"error_uri,omitempty"` +} diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 8d915e3658..149574eb41 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -15,6 +15,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/mail" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/lib/uiparam" "github.com/authgear/authgear-server/pkg/util/errorutil" @@ -674,7 +675,7 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA errorDescription := inputOAuth.GetOAuthErrorDescription() errorURI := inputOAuth.GetOAuthErrorURI() - return nil, sso.NewOAuthError(oauthError, errorDescription, errorURI) + return nil, oauthrelyingpartyutil.NewOAuthError(oauthError, errorDescription, errorURI) } oauthProvider := deps.OAuthProviderFactory.NewOAuthProvider(opts.Alias) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 7a91f5ad68..69672bea64 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -7,6 +7,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -61,7 +62,7 @@ func (f *ADFSImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param return } - var tokenResp AccessTokenResp + var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 74a60f51cd..7ee2aafb0f 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -12,6 +12,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/duration" @@ -98,7 +99,7 @@ func (f *AppleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param return } - var tokenResp AccessTokenResp + var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := appleOIDCConfig.ExchangeCode( f.HTTPClient, f.Clock, diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 8d06183999..7af1743c93 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -8,6 +8,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" ) @@ -70,7 +71,7 @@ func (f *Azureadb2cImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, return } - var tokenResp AccessTokenResp + var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index b571d12c16..c337b8cb31 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -8,6 +8,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" ) @@ -103,7 +104,7 @@ func (f *Azureadv2Impl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p return } - var tokenResp AccessTokenResp + var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, diff --git a/pkg/lib/authn/sso/error.go b/pkg/lib/authn/sso/error.go index e5d5f4114d..e8cfb7af8d 100644 --- a/pkg/lib/authn/sso/error.go +++ b/pkg/lib/authn/sso/error.go @@ -6,29 +6,3 @@ import ( var InvalidConfiguration = apierrors.InternalError.WithReason("InvalidConfiguration") var OAuthProtocolError = apierrors.BadRequest.WithReason("OAuthProtocolError") - -var OAuthError = apierrors.BadRequest.WithReason("OAuthError") - -func NewOAuthError(errorString string, errorDescription string, errorURI string) error { - msg := errorString - if errorDescription != "" { - msg += ": " + errorDescription - } - - return OAuthError.NewWithInfo(msg, apierrors.Details{ - "error": errorString, - "error_description": errorDescription, - "error_uri": errorURI, - }) -} - -// oauthErrorResp is a helper struct for deserialization purpose. -type oauthErrorResp struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description,omitempty"` - ErrorURI string `json:"error_uri,omitempty"` -} - -func (r *oauthErrorResp) AsError() error { - return NewOAuthError(r.Error, r.ErrorDescription, r.ErrorURI) -} diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 1882acfb55..3d453bbac3 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -6,6 +6,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" ) @@ -47,8 +48,8 @@ func (f *FacebookImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthIn func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { authInfo = AuthInfo{} - accessTokenResp, err := fetchAccessTokenResp( - f.HTTPClient, + accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( + f.HTTPClient.Client, r.Code, facebookTokenURL, param.RedirectURI, diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index d2653e3665..9993b2cb80 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -11,6 +11,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/errorutil" ) @@ -97,7 +98,7 @@ func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p return } -func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInfoParam) (accessTokenResp AccessTokenResp, err error) { +func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInfoParam) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) q.Set("client_id", g.ProviderConfig.ClientID()) q.Set("client_secret", g.Credentials.ClientSecret) @@ -122,18 +123,18 @@ func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInf return } } else { - var errResp oauthErrorResp + var errResp oauthrelyingparty.ErrorResponse err = json.NewDecoder(resp.Body).Decode(&errResp) if err != nil { return } - err = errResp.AsError() + err = oauthrelyingpartyutil.ErrorResponseAsError(errResp) } return } -func (g *GithubImpl) fetchUserInfo(accessTokenResp AccessTokenResp) (userProfile map[string]interface{}, err error) { +func (g *GithubImpl) fetchUserInfo(accessTokenResp oauthrelyingpartyutil.AccessTokenResp) (userProfile map[string]interface{}, err error) { tokenType := accessTokenResp.TokenType() accessTokenValue := accessTokenResp.AccessToken() authorizationHeader := fmt.Sprintf("%s %s", tokenType, accessTokenValue) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index d751348309..8b3d3b0748 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -6,6 +6,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" ) @@ -57,7 +58,7 @@ func (f *GoogleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, para return } - var tokenResp AccessTokenResp + var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := d.ExchangeCode( f.HTTPClient, f.Clock, diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 7834bd960f..4021896b92 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -4,6 +4,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" ) const ( @@ -43,8 +44,8 @@ func (f *LinkedInImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthIn } func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - accessTokenResp, err := fetchAccessTokenResp( - f.HTTPClient, + accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( + f.HTTPClient.Client, r.Code, linkedinTokenURL, param.RedirectURI, diff --git a/pkg/lib/authn/sso/oidc.go b/pkg/lib/authn/sso/oidc.go index 84c683b660..6ff3c8d239 100644 --- a/pkg/lib/authn/sso/oidc.go +++ b/pkg/lib/authn/sso/oidc.go @@ -11,6 +11,8 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwsutil" @@ -83,7 +85,7 @@ func (d *OIDCDiscoveryDocument) ExchangeCode( clientSecret string, redirectURI string, nonce string, - tokenResp *AccessTokenResp, + tokenResp *oauthrelyingpartyutil.AccessTokenResp, ) (jwt.Token, error) { body := url.Values{} body.Set("grant_type", "authorization_code") @@ -104,12 +106,12 @@ func (d *OIDCDiscoveryDocument) ExchangeCode( return nil, err } } else { - var errorResp oauthErrorResp + var errorResp oauthrelyingparty.ErrorResponse err = json.NewDecoder(resp.Body).Decode(&errorResp) if err != nil { return nil, err } - err = errorResp.AsError() + err = oauthrelyingpartyutil.ErrorResponseAsError(errorResp) return nil, err } diff --git a/pkg/lib/authn/sso/user_profile.go b/pkg/lib/authn/sso/user_profile.go index e08a3744ef..ee774e1da1 100644 --- a/pkg/lib/authn/sso/user_profile.go +++ b/pkg/lib/authn/sso/user_profile.go @@ -4,11 +4,13 @@ import ( "encoding/json" "fmt" "net/http" + + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" ) func fetchUserProfile( client OAuthHTTPClient, - accessTokenResp AccessTokenResp, + accessTokenResp oauthrelyingpartyutil.AccessTokenResp, userProfileURL string, ) (userProfile map[string]interface{}, err error) { tokenType := accessTokenResp.TokenType() diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 711ca7168d..d924b791a3 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -10,6 +10,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/interaction" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" ) @@ -59,7 +60,7 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra // Handle provider error if oauthError != "" { - return nil, sso.NewOAuthError(oauthError, errorDescription, errorURI) + return nil, oauthrelyingpartyutil.NewOAuthError(oauthError, errorDescription, errorURI) } if nonceSource == "" { diff --git a/pkg/lib/authn/sso/access_token.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go similarity index 89% rename from pkg/lib/authn/sso/access_token.go rename to pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go index 8bc607de7f..0c590274d3 100644 --- a/pkg/lib/authn/sso/access_token.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go @@ -1,10 +1,13 @@ -package sso +package oauthrelyingpartyutil import ( "encoding/json" + "net/http" "net/url" "strconv" "strings" + + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" ) type AccessTokenResp map[string]interface{} @@ -76,8 +79,8 @@ func (r AccessTokenResp) TokenType() string { } } -func fetchAccessTokenResp( - client OAuthHTTPClient, +func FetchAccessTokenResp( + client *http.Client, code string, accessTokenURL string, redirectURL string, @@ -106,12 +109,12 @@ func fetchAccessTokenResp( return } } else { // normally 400 Bad Request - var errResp oauthErrorResp + var errResp oauthrelyingparty.ErrorResponse err = json.NewDecoder(resp.Body).Decode(&errResp) if err != nil { return } - err = errResp.AsError() + err = ErrorResponseAsError(errResp) } return diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go new file mode 100644 index 0000000000..9e4f4ffacb --- /dev/null +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go @@ -0,0 +1,25 @@ +package oauthrelyingpartyutil + +import ( + "github.com/authgear/authgear-server/pkg/api/apierrors" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" +) + +var OAuthError = apierrors.BadRequest.WithReason("OAuthError") + +func NewOAuthError(errorString string, errorDescription string, errorURI string) error { + msg := errorString + if errorDescription != "" { + msg += ": " + errorDescription + } + + return OAuthError.NewWithInfo(msg, apierrors.Details{ + "error": errorString, + "error_description": errorDescription, + "error_uri": errorURI, + }) +} + +func ErrorResponseAsError(errResp oauthrelyingparty.ErrorResponse) error { + return NewOAuthError(errResp.Error, errResp.ErrorDescription, errResp.ErrorURI) +} From d4c7a17b1842a3718fc5600d5ff9faac12b0cafa Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 9 May 2024 19:12:56 +0800 Subject: [PATCH 03/30] Move user_profile.go to oauthrelyingpartyutil --- pkg/lib/authn/sso/facebook.go | 2 +- pkg/lib/authn/sso/linkedin.go | 4 ++-- .../oauthrelyingpartyutil}/user_profile.go | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) rename pkg/lib/{authn/sso => oauthrelyingparty/oauthrelyingpartyutil}/user_profile.go (79%) diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 3d453bbac3..d6072c4293 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -88,7 +88,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, // "short_name": "John" // } - userProfile, err := fetchUserProfile(f.HTTPClient, accessTokenResp, userProfileURL.String()) + userProfile, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, userProfileURL.String()) if err != nil { return } diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 4021896b92..44b9d3bd62 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -56,12 +56,12 @@ func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, return } - meResponse, err := fetchUserProfile(f.HTTPClient, accessTokenResp, linkedinMeURL) + meResponse, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, linkedinMeURL) if err != nil { return } - contactResponse, err := fetchUserProfile(f.HTTPClient, accessTokenResp, linkedinContactURL) + contactResponse, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, linkedinContactURL) if err != nil { return } diff --git a/pkg/lib/authn/sso/user_profile.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/user_profile.go similarity index 79% rename from pkg/lib/authn/sso/user_profile.go rename to pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/user_profile.go index ee774e1da1..8aea2da4ee 100644 --- a/pkg/lib/authn/sso/user_profile.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/user_profile.go @@ -1,16 +1,14 @@ -package sso +package oauthrelyingpartyutil import ( "encoding/json" "fmt" "net/http" - - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" ) -func fetchUserProfile( - client OAuthHTTPClient, - accessTokenResp oauthrelyingpartyutil.AccessTokenResp, +func FetchUserProfile( + client *http.Client, + accessTokenResp AccessTokenResp, userProfileURL string, ) (userProfile map[string]interface{}, err error) { tokenType := accessTokenResp.TokenType() From 1bf0236e05c53b2e7317a05e02f052693ecc1f9e Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 12:32:08 +0800 Subject: [PATCH 04/30] Move authurl.go to oauthrelyingpartyutil --- pkg/api/oauthrelyingparty/response_mode.go | 6 ++++++ pkg/api/oauthrelyingparty/response_type.go | 5 +++++ pkg/auth/handler/webapp/authflow_login.go | 4 ++-- pkg/auth/handler/webapp/authflow_promote.go | 4 ++-- pkg/auth/handler/webapp/authflow_signup.go | 4 ++-- .../authflowv2/internal_signup_login.go | 4 ++-- pkg/auth/handler/webapp/authflowv2/login.go | 4 ++-- pkg/auth/handler/webapp/authflowv2/promote.go | 4 ++-- .../declarative/input_interface.go | 3 +-- .../declarative/input_step_identify.go | 12 ++++++------ .../input_take_oauth_authorization_request.go | 12 ++++++------ .../declarative/node_lookup_identity_oauth.go | 3 +-- .../declarative/node_oauth.go | 11 +++++------ .../node_promote_identity_oauth.go | 3 +-- .../declarative/synthetic_input_oauth.go | 5 ++--- .../declarative/utils_common.go | 2 +- pkg/lib/authn/sso/adfs.go | 4 ++-- pkg/lib/authn/sso/adfs_test.go | 2 +- pkg/lib/authn/sso/apple.go | 4 ++-- pkg/lib/authn/sso/apple_test.go | 2 +- pkg/lib/authn/sso/azureadb2c.go | 4 ++-- pkg/lib/authn/sso/azureadb2c_test.go | 2 +- pkg/lib/authn/sso/azureadv2.go | 4 ++-- pkg/lib/authn/sso/azureadv2_test.go | 2 +- pkg/lib/authn/sso/facebook.go | 4 ++-- pkg/lib/authn/sso/github.go | 2 +- pkg/lib/authn/sso/google.go | 4 ++-- pkg/lib/authn/sso/google_test.go | 2 +- pkg/lib/authn/sso/linkedin.go | 4 ++-- pkg/lib/authn/sso/oauth_provider.go | 2 +- pkg/lib/authn/sso/oidc.go | 4 ++-- pkg/lib/authn/sso/wechat.go | 5 +++-- .../nodes/use_identity_oauth_provider.go | 2 +- .../authorization_url.go} | 19 +++---------------- 34 files changed, 76 insertions(+), 82 deletions(-) create mode 100644 pkg/api/oauthrelyingparty/response_mode.go create mode 100644 pkg/api/oauthrelyingparty/response_type.go rename pkg/lib/{authn/sso/authurl.go => oauthrelyingparty/oauthrelyingpartyutil/authorization_url.go} (78%) diff --git a/pkg/api/oauthrelyingparty/response_mode.go b/pkg/api/oauthrelyingparty/response_mode.go new file mode 100644 index 0000000000..8901b3f129 --- /dev/null +++ b/pkg/api/oauthrelyingparty/response_mode.go @@ -0,0 +1,6 @@ +package oauthrelyingparty + +const ( + ResponseModeFormPost = "form_post" + ResponseModeQuery = "query" +) diff --git a/pkg/api/oauthrelyingparty/response_type.go b/pkg/api/oauthrelyingparty/response_type.go new file mode 100644 index 0000000000..a55fad1101 --- /dev/null +++ b/pkg/api/oauthrelyingparty/response_type.go @@ -0,0 +1,5 @@ +package oauthrelyingparty + +const ( + ResponseTypeCode = "code" +) diff --git a/pkg/auth/handler/webapp/authflow_login.go b/pkg/auth/handler/webapp/authflow_login.go index 600ca93791..ac7217a604 100644 --- a/pkg/auth/handler/webapp/authflow_login.go +++ b/pkg/auth/handler/webapp/authflow_login.go @@ -6,10 +6,10 @@ import ( "net/http" "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/meter" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/httputil" @@ -88,7 +88,7 @@ func (h *AuthflowLoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.ReplaceScreen(r, s, authflow.FlowTypeSignupLogin, input) diff --git a/pkg/auth/handler/webapp/authflow_promote.go b/pkg/auth/handler/webapp/authflow_promote.go index 52b0b994fd..79d2cc068c 100644 --- a/pkg/auth/handler/webapp/authflow_promote.go +++ b/pkg/auth/handler/webapp/authflow_promote.go @@ -4,10 +4,10 @@ import ( "net/http" "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" @@ -82,7 +82,7 @@ func (h *AuthflowPromoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.AdvanceWithInput(r, s, screen, input, nil) diff --git a/pkg/auth/handler/webapp/authflow_signup.go b/pkg/auth/handler/webapp/authflow_signup.go index 6e84a40bd3..73c206c70c 100644 --- a/pkg/auth/handler/webapp/authflow_signup.go +++ b/pkg/auth/handler/webapp/authflow_signup.go @@ -5,10 +5,10 @@ import ( "net/http" "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/meter" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/httputil" @@ -98,7 +98,7 @@ func (h *AuthflowSignupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.ReplaceScreen(r, s, authflow.FlowTypeSignupLogin, input) diff --git a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go index 9e947e03ed..416adbd5ea 100644 --- a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go +++ b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/meter" "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" @@ -115,7 +115,7 @@ func (h *InternalAuthflowV2SignupLoginHandler) ServeHTTP(w http.ResponseWriter, "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.ReplaceScreen(r, s, authflow.FlowTypeSignupLogin, input) diff --git a/pkg/auth/handler/webapp/authflowv2/login.go b/pkg/auth/handler/webapp/authflowv2/login.go index 22e12dde50..3a33e9e466 100644 --- a/pkg/auth/handler/webapp/authflowv2/login.go +++ b/pkg/auth/handler/webapp/authflowv2/login.go @@ -6,12 +6,12 @@ import ( "net/http" "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/meter" "github.com/authgear/authgear-server/pkg/util/httputil" @@ -98,7 +98,7 @@ func (h *AuthflowV2LoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.ReplaceScreen(r, s, authflow.FlowTypeSignupLogin, input) diff --git a/pkg/auth/handler/webapp/authflowv2/promote.go b/pkg/auth/handler/webapp/authflowv2/promote.go index 6291cad848..2651eac964 100644 --- a/pkg/auth/handler/webapp/authflowv2/promote.go +++ b/pkg/auth/handler/webapp/authflowv2/promote.go @@ -4,11 +4,11 @@ import ( "net/http" "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -83,7 +83,7 @@ func (h *AuthflowV2PromoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ "identification": "oauth", "alias": providerAlias, "redirect_uri": callbackURL, - "response_mode": string(sso.ResponseModeFormPost), + "response_mode": oauthrelyingparty.ResponseModeFormPost, } result, err := h.Controller.AdvanceWithInput(r, s, screen, input, nil) diff --git a/pkg/lib/authenticationflow/declarative/input_interface.go b/pkg/lib/authenticationflow/declarative/input_interface.go index 5a90b7f2b4..2571e40b0f 100644 --- a/pkg/lib/authenticationflow/declarative/input_interface.go +++ b/pkg/lib/authenticationflow/declarative/input_interface.go @@ -6,7 +6,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/attrs" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -47,7 +46,7 @@ type inputTakeIDToken interface { type inputTakeOAuthAuthorizationRequest interface { GetOAuthAlias() string GetOAuthRedirectURI() string - GetOAuthResponseMode() sso.ResponseMode + GetOAuthResponseMode() string // We used to accept `state`. // But it turns out to be confusing. // `state` is used to maintain state between the request and the callback. diff --git a/pkg/lib/authenticationflow/declarative/input_step_identify.go b/pkg/lib/authenticationflow/declarative/input_step_identify.go index 7ae393bc8b..0e22bc9920 100644 --- a/pkg/lib/authenticationflow/declarative/input_step_identify.go +++ b/pkg/lib/authenticationflow/declarative/input_step_identify.go @@ -5,8 +5,8 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -70,7 +70,7 @@ func (i *InputSchemaStepIdentify) SchemaBuilder() validation.SchemaBuilder { // response_mode is optional. b.Properties().Property("response_mode", validation.SchemaBuilder{}. Type(validation.TypeString). - Enum(sso.ResponseModeFormPost, sso.ResponseModeQuery)) + Enum(oauthrelyingparty.ResponseModeFormPost, oauthrelyingparty.ResponseModeQuery)) setRequiredAndAppendOneOf() case config.AuthenticationFlowIdentificationPasskey: @@ -108,9 +108,9 @@ type InputStepIdentify struct { LoginID string `json:"login,omitempty"` - Alias string `json:"alias,omitempty"` - RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + Alias string `json:"alias,omitempty"` + RedirectURI string `json:"redirect_uri,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.Input = &InputStepIdentify{} @@ -141,6 +141,6 @@ func (i *InputStepIdentify) GetOAuthRedirectURI() string { return i.RedirectURI } -func (i *InputStepIdentify) GetOAuthResponseMode() sso.ResponseMode { +func (i *InputStepIdentify) GetOAuthResponseMode() string { return i.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go index b96cc421c9..c7bd4e9507 100644 --- a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go +++ b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go @@ -5,8 +5,8 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -34,7 +34,7 @@ func (i *InputSchemaTakeOAuthAuthorizationRequest) SchemaBuilder() validation.Sc b.Properties().Property("redirect_uri", validation.SchemaBuilder{}.Type(validation.TypeString).Format("uri")) b.Properties().Property("response_mode", validation.SchemaBuilder{}. Type(validation.TypeString). - Enum(sso.ResponseModeFormPost, sso.ResponseModeQuery)) + Enum(oauthrelyingparty.ResponseModeFormPost, oauthrelyingparty.ResponseModeQuery)) var enumValues []interface{} for _, c := range i.OAuthOptions { @@ -57,9 +57,9 @@ func (i *InputSchemaTakeOAuthAuthorizationRequest) MakeInput(rawMessage json.Raw } type InputTakeOAuthAuthorizationRequest struct { - Alias string `json:"alias"` - RedirectURI string `json:"redirect_uri"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + Alias string `json:"alias"` + RedirectURI string `json:"redirect_uri"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.Input = &InputTakeOAuthAuthorizationRequest{} @@ -75,6 +75,6 @@ func (i *InputTakeOAuthAuthorizationRequest) GetOAuthRedirectURI() string { return i.RedirectURI } -func (i *InputTakeOAuthAuthorizationRequest) GetOAuthResponseMode() sso.ResponseMode { +func (i *InputTakeOAuthAuthorizationRequest) GetOAuthResponseMode() string { return i.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/node_lookup_identity_oauth.go b/pkg/lib/authenticationflow/declarative/node_lookup_identity_oauth.go index b62cfe7849..03c20a4e85 100644 --- a/pkg/lib/authenticationflow/declarative/node_lookup_identity_oauth.go +++ b/pkg/lib/authenticationflow/declarative/node_lookup_identity_oauth.go @@ -9,7 +9,6 @@ import ( "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -22,7 +21,7 @@ type NodeLookupIdentityOAuth struct { SyntheticInput *InputStepIdentify `json:"synthetic_input,omitempty"` Alias string `json:"alias,omitempty"` RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.NodeSimple = &NodeLookupIdentityOAuth{} diff --git a/pkg/lib/authenticationflow/declarative/node_oauth.go b/pkg/lib/authenticationflow/declarative/node_oauth.go index dc593288fd..2abb840790 100644 --- a/pkg/lib/authenticationflow/declarative/node_oauth.go +++ b/pkg/lib/authenticationflow/declarative/node_oauth.go @@ -7,7 +7,6 @@ import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" ) func init() { @@ -15,11 +14,11 @@ func init() { } type NodeOAuth struct { - JSONPointer jsonpointer.T `json:"json_pointer,omitempty"` - NewUserID string `json:"new_user_id,omitempty"` - Alias string `json:"alias,omitempty"` - RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + JSONPointer jsonpointer.T `json:"json_pointer,omitempty"` + NewUserID string `json:"new_user_id,omitempty"` + Alias string `json:"alias,omitempty"` + RedirectURI string `json:"redirect_uri,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.NodeSimple = &NodeOAuth{} diff --git a/pkg/lib/authenticationflow/declarative/node_promote_identity_oauth.go b/pkg/lib/authenticationflow/declarative/node_promote_identity_oauth.go index 216429b1c4..72c8000103 100644 --- a/pkg/lib/authenticationflow/declarative/node_promote_identity_oauth.go +++ b/pkg/lib/authenticationflow/declarative/node_promote_identity_oauth.go @@ -10,7 +10,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/util/slice" ) @@ -24,7 +23,7 @@ type NodePromoteIdentityOAuth struct { SyntheticInput *InputStepIdentify `json:"synthetic_input,omitempty"` Alias string `json:"alias,omitempty"` RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.NodeSimple = &NodePromoteIdentityOAuth{} diff --git a/pkg/lib/authenticationflow/declarative/synthetic_input_oauth.go b/pkg/lib/authenticationflow/declarative/synthetic_input_oauth.go index 8cf6f2113f..b812b3eed8 100644 --- a/pkg/lib/authenticationflow/declarative/synthetic_input_oauth.go +++ b/pkg/lib/authenticationflow/declarative/synthetic_input_oauth.go @@ -3,7 +3,6 @@ package declarative import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -12,7 +11,7 @@ type SyntheticInputOAuth struct { Alias string `json:"alias,omitempty"` State string `json:"state,omitempty"` RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` IdentitySpec *identity.Spec `json:"identity_spec,omitempty"` } @@ -39,7 +38,7 @@ func (i *SyntheticInputOAuth) GetOAuthRedirectURI() string { return i.RedirectURI } -func (i *SyntheticInputOAuth) GetOAuthResponseMode() sso.ResponseMode { +func (i *SyntheticInputOAuth) GetOAuthResponseMode() string { return i.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 149574eb41..f8e9d98790 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -720,7 +720,7 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA type GetOAuthDataOptions struct { RedirectURI string Alias string - ResponseMode sso.ResponseMode + ResponseMode string } func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAuthDataOptions) (data OAuthData, err error) { diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 69672bea64..e701ba276b 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -34,11 +34,11 @@ func (f *ADFSImpl) GetAuthURL(param GetAuthURLParam) (string, error) { if err != nil { return "", err } - return c.MakeOAuthURL(AuthorizationURLParams{ + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/authn/sso/adfs_test.go index ff3b022205..9479d2300b 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/authn/sso/adfs_test.go @@ -37,7 +37,7 @@ func TestADFSImpl(t *testing.T) { u, err := g.GetAuthURL(GetAuthURLParam{ RedirectURI: "https://localhost/", - ResponseMode: ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 7ee2aafb0f..f48e21003b 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -72,11 +72,11 @@ func (f *AppleImpl) Config() oauthrelyingparty.ProviderConfig { } func (f *AppleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { - return appleOIDCConfig.MakeOAuthURL(AuthorizationURLParams{ + return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/authn/sso/apple_test.go index 876541092c..e74bb04e63 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/authn/sso/apple_test.go @@ -21,7 +21,7 @@ func TestAppleImpl(t *testing.T) { u, err := g.GetAuthURL(GetAuthURLParam{ RedirectURI: "https://localhost/", - ResponseMode: ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 7af1743c93..b21f16dd5e 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -44,11 +44,11 @@ func (f *Azureadb2cImpl) GetAuthURL(param GetAuthURLParam) (string, error) { if err != nil { return "", err } - return c.MakeOAuthURL(AuthorizationURLParams{ + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/authn/sso/azureadb2c_test.go index 8cc869d1fe..c73d09eded 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/authn/sso/azureadb2c_test.go @@ -38,7 +38,7 @@ func TestAzureadb2cImpl(t *testing.T) { u, err := g.GetAuthURL(GetAuthURLParam{ RedirectURI: "https://localhost/", - ResponseMode: ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index c337b8cb31..8603dcc9c3 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -77,11 +77,11 @@ func (f *Azureadv2Impl) GetAuthURL(param GetAuthURLParam) (string, error) { if err != nil { return "", err } - return c.MakeOAuthURL(AuthorizationURLParams{ + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/authn/sso/azureadv2_test.go index ee5a6469b1..e111bed97e 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/authn/sso/azureadv2_test.go @@ -101,7 +101,7 @@ func TestAzureadv2Impl(t *testing.T) { u, err := g.GetAuthURL(GetAuthURLParam{ RedirectURI: "https://localhost/", - ResponseMode: ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index d6072c4293..19ec608e05 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -29,11 +29,11 @@ func (f *FacebookImpl) Config() oauthrelyingparty.ProviderConfig { } func (f *FacebookImpl) GetAuthURL(param GetAuthURLParam) (string, error) { - return MakeAuthorizationURL(facebookAuthorizationURL, AuthorizationURLParams{ + return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 9993b2cb80..441af092f4 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -35,7 +35,7 @@ func (g *GithubImpl) Config() oauthrelyingparty.ProviderConfig { func (g *GithubImpl) GetAuthURL(param GetAuthURLParam) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity - return MakeAuthorizationURL(githubAuthorizationURL, AuthorizationURLParams{ + return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: g.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: g.ProviderConfig.Scope(), diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 8b3d3b0748..20b3e4b3c8 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -27,11 +27,11 @@ func (f *GoogleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { if err != nil { return "", err } - return d.MakeOAuthURL(AuthorizationURLParams{ + return d.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, Nonce: param.Nonce, diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/authn/sso/google_test.go index 2560702315..e45ea1bc98 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/authn/sso/google_test.go @@ -91,7 +91,7 @@ func TestGoogleImpl(t *testing.T) { u, err := g.GetAuthURL(GetAuthURLParam{ RedirectURI: "https://localhost/", - ResponseMode: ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 44b9d3bd62..535fff1292 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -27,11 +27,11 @@ func (f *LinkedInImpl) Config() oauthrelyingparty.ProviderConfig { } func (f *LinkedInImpl) GetAuthURL(param GetAuthURLParam) (string, error) { - return MakeAuthorizationURL(linkedinAuthorizationURL, AuthorizationURLParams{ + return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: f.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, Prompt: f.GetPrompt(param.Prompt), diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index af4f923876..8d693e4b3a 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -18,7 +18,7 @@ import ( type GetAuthURLParam struct { RedirectURI string - ResponseMode ResponseMode + ResponseMode string Nonce string State string Prompt []string diff --git a/pkg/lib/authn/sso/oidc.go b/pkg/lib/authn/sso/oidc.go index 6ff3c8d239..28719e2242 100644 --- a/pkg/lib/authn/sso/oidc.go +++ b/pkg/lib/authn/sso/oidc.go @@ -58,8 +58,8 @@ func FetchOIDCDiscoveryDocument(client OAuthHTTPClient, endpoint string) (*OIDCD return &document, nil } -func (d *OIDCDiscoveryDocument) MakeOAuthURL(params AuthorizationURLParams) string { - return MakeAuthorizationURL(d.AuthorizationEndpoint, params.Query()) +func (d *OIDCDiscoveryDocument) MakeOAuthURL(params oauthrelyingpartyutil.AuthorizationURLParams) string { + return oauthrelyingpartyutil.MakeAuthorizationURL(d.AuthorizationEndpoint, params.Query()) } func (d *OIDCDiscoveryDocument) FetchJWKs(client OAuthHTTPClient) (jwk.Set, error) { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 9e0932b540..18413c4d37 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -4,6 +4,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) @@ -23,12 +24,12 @@ func (w *WechatImpl) Config() oauthrelyingparty.ProviderConfig { } func (w *WechatImpl) GetAuthURL(param GetAuthURLParam) (string, error) { - return MakeAuthorizationURL(wechatAuthorizationURL, AuthorizationURLParams{ + return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. WechatAppID: w.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, Scope: w.ProviderConfig.Scope(), - ResponseType: ResponseTypeCode, + ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, Prompt: w.GetPrompt(param.Prompt), diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 638a946004..cfdf1b8c38 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -82,7 +82,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra param := sso.GetAuthURLParam{ RedirectURI: redirectURIForOAuthProvider, // We use response_mode=form_post if it is supported. - ResponseMode: sso.ResponseModeFormPost, + ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: nonce, Prompt: input.GetPrompt(), State: ctx.WebSessionID, diff --git a/pkg/lib/authn/sso/authurl.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/authorization_url.go similarity index 78% rename from pkg/lib/authn/sso/authurl.go rename to pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/authorization_url.go index b347d364ca..28be954eec 100644 --- a/pkg/lib/authn/sso/authurl.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/authorization_url.go @@ -1,29 +1,16 @@ -package sso +package oauthrelyingpartyutil import ( "net/url" "strings" ) -type ResponseType string - -const ( - ResponseTypeCode ResponseType = "code" -) - -type ResponseMode string - -const ( - ResponseModeFormPost ResponseMode = "form_post" - ResponseModeQuery ResponseMode = "query" -) - type AuthorizationURLParams struct { ClientID string RedirectURI string Scope []string - ResponseType ResponseType - ResponseMode ResponseMode + ResponseType string + ResponseMode string State string Prompt []string Nonce string From dfece4fa5035d27ac651dca1f6a8aec40a028d16 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:07:56 +0800 Subject: [PATCH 05/30] Remove dependency on pkg/lib/config in legacy implementations --- pkg/lib/authn/sso/adfs.go | 5 ++--- pkg/lib/authn/sso/apple.go | 5 ++--- pkg/lib/authn/sso/azureadb2c.go | 5 ++--- pkg/lib/authn/sso/azureadv2.go | 5 ++--- pkg/lib/authn/sso/facebook.go | 7 +++---- pkg/lib/authn/sso/github.go | 5 ++--- pkg/lib/authn/sso/google.go | 5 ++--- pkg/lib/authn/sso/linkedin.go | 5 ++--- pkg/lib/authn/sso/oauth_provider.go | 18 +++++++++--------- pkg/lib/authn/sso/wechat.go | 5 ++--- 10 files changed, 28 insertions(+), 37 deletions(-) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index e701ba276b..e5d52069ba 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -5,7 +5,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" @@ -15,7 +14,7 @@ import ( type ADFSImpl struct { Clock clock.Clock ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -69,7 +68,7 @@ func (f *ADFSImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param r.Code, keySet, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index f48e21003b..05e2dd1803 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -10,7 +10,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" @@ -28,7 +27,7 @@ var appleOIDCConfig = OIDCDiscoveryDocument{ type AppleImpl struct { Clock clock.Clock ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -38,7 +37,7 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { keyID := apple.ProviderConfig(f.ProviderConfig).KeyID() // https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens - key, err := crypto.ParseAppleP8PrivateKey([]byte(f.Credentials.ClientSecret)) + key, err := crypto.ParseAppleP8PrivateKey([]byte(f.ClientSecret)) if err != nil { return } diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index b21f16dd5e..f631497aae 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -6,7 +6,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" @@ -15,7 +14,7 @@ import ( type Azureadb2cImpl struct { Clock clock.Clock ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -78,7 +77,7 @@ func (f *Azureadb2cImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, r.Code, keySet, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 8603dcc9c3..692af92b20 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -6,7 +6,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" @@ -15,7 +14,7 @@ import ( type Azureadv2Impl struct { Clock clock.Clock ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -111,7 +110,7 @@ func (f *Azureadv2Impl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p r.Code, keySet, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 19ec608e05..30a8480e7a 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -5,7 +5,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" ) @@ -19,7 +18,7 @@ const ( type FacebookImpl struct { ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -54,7 +53,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, facebookTokenURL, param.RedirectURI, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, ) if err != nil { return @@ -65,7 +64,7 @@ func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, return } q := userProfileURL.Query() - appSecretProof := crypto.HMACSHA256String([]byte(f.Credentials.ClientSecret), []byte(accessTokenResp.AccessToken())) + appSecretProof := crypto.HMACSHA256String([]byte(f.ClientSecret), []byte(accessTokenResp.AccessToken())) q.Set("appsecret_proof", appSecretProof) userProfileURL.RawQuery = q.Encode() diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 441af092f4..35e085c428 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -10,7 +10,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/errorutil" ) @@ -24,7 +23,7 @@ const ( type GithubImpl struct { ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -101,7 +100,7 @@ func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, p func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInfoParam) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) q.Set("client_id", g.ProviderConfig.ClientID()) - q.Set("client_secret", g.Credentials.ClientSecret) + q.Set("client_secret", g.ClientSecret) q.Set("code", r.Code) q.Set("redirect_uri", param.RedirectURI) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 20b3e4b3c8..e2fed20a51 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -5,7 +5,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/clock" ) @@ -17,7 +16,7 @@ const ( type GoogleImpl struct { Clock clock.Clock ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -65,7 +64,7 @@ func (f *GoogleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, para r.Code, keySet, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 535fff1292..470a9d44cd 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -3,7 +3,6 @@ package sso import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" ) @@ -17,7 +16,7 @@ const ( type LinkedInImpl struct { ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -50,7 +49,7 @@ func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, linkedinTokenURL, param.RedirectURI, f.ProviderConfig.ClientID(), - f.Credentials.ClientSecret, + f.ClientSecret, ) if err != nil { return diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 8d693e4b3a..fa659f3547 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -89,28 +89,28 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return &GoogleImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } case facebook.Type: return &FacebookImpl{ ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } case github.Type: return &GithubImpl{ ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } case linkedin.Type: return &LinkedInImpl{ ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } @@ -118,7 +118,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return &Azureadv2Impl{ Clock: p.Clock, ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } @@ -126,7 +126,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return &Azureadb2cImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } @@ -134,7 +134,7 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return &ADFSImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } @@ -142,14 +142,14 @@ func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { return &AppleImpl{ Clock: p.Clock, ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } case wechat.Type: return &WechatImpl{ ProviderConfig: *providerConfig, - Credentials: *credentials, + ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 18413c4d37..7c31f8498e 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -3,7 +3,6 @@ package sso import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) @@ -14,7 +13,7 @@ const ( type WechatImpl struct { ProviderConfig oauthrelyingparty.ProviderConfig - Credentials config.OAuthSSOProviderCredentialsItem + ClientSecret string StandardAttributesNormalizer StandardAttributesNormalizer HTTPClient OAuthHTTPClient } @@ -46,7 +45,7 @@ func (w *WechatImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, _ w.HTTPClient, r.Code, w.ProviderConfig.ClientID(), - w.Credentials.ClientSecret, + w.ClientSecret, ) if err != nil { return From c5678829880f0908c71f77f92c4501b6870face6 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:14:59 +0800 Subject: [PATCH 06/30] Remove unnecessary interfaces NonOpenIDConnectProvider and OpenIDConnectProvider --- pkg/lib/authn/sso/adfs.go | 7 +------ pkg/lib/authn/sso/apple.go | 7 +------ pkg/lib/authn/sso/azureadb2c.go | 7 +------ pkg/lib/authn/sso/azureadv2.go | 7 +------ pkg/lib/authn/sso/facebook.go | 7 +------ pkg/lib/authn/sso/github.go | 7 +------ pkg/lib/authn/sso/google.go | 7 +------ pkg/lib/authn/sso/linkedin.go | 7 +------ pkg/lib/authn/sso/oauth_provider.go | 21 --------------------- pkg/lib/authn/sso/wechat.go | 9 ++------- 10 files changed, 10 insertions(+), 76 deletions(-) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index e5d52069ba..2dfaf43ae6 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -46,10 +46,6 @@ func (f *ADFSImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *ADFSImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.OpenIDConnectGetAuthInfo(r, param) -} - -func (f *ADFSImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -145,6 +141,5 @@ func (f *ADFSImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &ADFSImpl{} - _ OpenIDConnectProvider = &ADFSImpl{} + _ OAuthProvider = &ADFSImpl{} ) diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 05e2dd1803..3cd2c841be 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -84,10 +84,6 @@ func (f *AppleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *AppleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.OpenIDConnectGetAuthInfo(r, param) -} - -func (f *AppleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { keySet, err := appleOIDCConfig.FetchJWKs(f.HTTPClient) if err != nil { return @@ -174,6 +170,5 @@ func (f *AppleImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &AppleImpl{} - _ OpenIDConnectProvider = &AppleImpl{} + _ OAuthProvider = &AppleImpl{} ) diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index f631497aae..9f352b26bb 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -56,10 +56,6 @@ func (f *Azureadb2cImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *Azureadb2cImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.OpenIDConnectGetAuthInfo(r, param) -} - -func (f *Azureadb2cImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -196,6 +192,5 @@ func (f *Azureadb2cImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &Azureadb2cImpl{} - _ OpenIDConnectProvider = &Azureadb2cImpl{} + _ OAuthProvider = &Azureadb2cImpl{} ) diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 692af92b20..93d0beefc2 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -89,10 +89,6 @@ func (f *Azureadv2Impl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *Azureadv2Impl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.OpenIDConnectGetAuthInfo(r, param) -} - -func (f *Azureadv2Impl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -171,6 +167,5 @@ func (f *Azureadv2Impl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &Azureadv2Impl{} - _ OpenIDConnectProvider = &Azureadv2Impl{} + _ OAuthProvider = &Azureadv2Impl{} ) diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 30a8480e7a..7075f6035c 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -41,10 +41,6 @@ func (f *FacebookImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *FacebookImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.NonOpenIDConnectGetAuthInfo(r, param) -} - -func (f *FacebookImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { authInfo = AuthInfo{} accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( @@ -140,6 +136,5 @@ func (f *FacebookImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &FacebookImpl{} - _ NonOpenIDConnectProvider = &FacebookImpl{} + _ OAuthProvider = &FacebookImpl{} ) diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 35e085c428..dc5802287c 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -47,10 +47,6 @@ func (g *GithubImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (g *GithubImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return g.NonOpenIDConnectGetAuthInfo(r, param) -} - -func (g *GithubImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { accessTokenResp, err := g.exchangeCode(r, param) if err != nil { return @@ -174,6 +170,5 @@ func (*GithubImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &GithubImpl{} - _ NonOpenIDConnectProvider = &GithubImpl{} + _ OAuthProvider = &GithubImpl{} ) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index e2fed20a51..8b7292401a 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -43,10 +43,6 @@ func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { } func (f *GoogleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.OpenIDConnectGetAuthInfo(r, param) -} - -func (f *GoogleImpl) OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return @@ -139,6 +135,5 @@ func (f *GoogleImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &GoogleImpl{} - _ OpenIDConnectProvider = &GoogleImpl{} + _ OAuthProvider = &GoogleImpl{} ) diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 470a9d44cd..5d66213d74 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -39,10 +39,6 @@ func (f *LinkedInImpl) GetAuthURL(param GetAuthURLParam) (string, error) { } func (f *LinkedInImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - return f.NonOpenIDConnectGetAuthInfo(r, param) -} - -func (f *LinkedInImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, r.Code, @@ -352,6 +348,5 @@ func decodeLinkedIn(userInfo map[string]interface{}) (string, stdattrs.T) { } var ( - _ OAuthProvider = &LinkedInImpl{} - _ NonOpenIDConnectProvider = &LinkedInImpl{} + _ OAuthProvider = &LinkedInImpl{} ) diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index fa659f3547..0f687d44af 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -41,27 +41,6 @@ type OAuthProvider interface { GetPrompt(prompt []string) []string } -// NonOpenIDConnectProvider are OAuth 2.0 provider that does not -// implement OpenID Connect or we do not implement yet. -// They are -// "facebook" -// "linkedin" -// "wechat" -type NonOpenIDConnectProvider interface { - NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) -} - -// OpenIDConnectProvider are OpenID Connect provider. -// They are -// "google" -// "apple" -// "azureadv2" -// "azureadb2c" -// "adfs" -type OpenIDConnectProvider interface { - OpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) -} - type StandardAttributesNormalizer interface { Normalize(stdattrs.T) error } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 7c31f8498e..4924e7a22d 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -36,11 +36,7 @@ func (w *WechatImpl) GetAuthURL(param GetAuthURLParam) (string, error) { }.Query()), nil } -func (w *WechatImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) { - return w.NonOpenIDConnectGetAuthInfo(r, param) -} - -func (w *WechatImpl) NonOpenIDConnectGetAuthInfo(r OAuthAuthorizationResponse, _ GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (w *WechatImpl) GetAuthInfo(r OAuthAuthorizationResponse, _ GetAuthInfoParam) (authInfo AuthInfo, err error) { accessTokenResp, err := wechatFetchAccessTokenResp( w.HTTPClient, r.Code, @@ -124,6 +120,5 @@ func (w *WechatImpl) GetPrompt(prompt []string) []string { } var ( - _ OAuthProvider = &WechatImpl{} - _ NonOpenIDConnectProvider = &WechatImpl{} + _ OAuthProvider = &WechatImpl{} ) From 234f9c0a8b6205d04ceb87af88ea1845bde19e7d Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:28:07 +0800 Subject: [PATCH 07/30] Remove GetPrompt from the interface This method is an implementation details of the provider. --- pkg/lib/authn/sso/adfs.go | 8 ++++---- pkg/lib/authn/sso/apple.go | 14 +++++--------- pkg/lib/authn/sso/azureadb2c.go | 4 ++-- pkg/lib/authn/sso/azureadv2.go | 14 +++++++------- pkg/lib/authn/sso/facebook.go | 13 +++++-------- pkg/lib/authn/sso/github.go | 5 ----- pkg/lib/authn/sso/google.go | 12 ++++++------ pkg/lib/authn/sso/linkedin.go | 13 +++++-------- pkg/lib/authn/sso/oauth_provider.go | 1 - pkg/lib/authn/sso/wechat.go | 12 ++++-------- 10 files changed, 38 insertions(+), 58 deletions(-) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 2dfaf43ae6..33cd4f2e59 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -40,7 +40,7 @@ func (f *ADFSImpl) GetAuthURL(param GetAuthURLParam) (string, error) { ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, - Prompt: f.GetPrompt(param.Prompt), + Prompt: f.getPrompt(param.Prompt), Nonce: param.Nonce, }), nil } @@ -129,9 +129,9 @@ func (f *ADFSImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoPa return } -func (f *ADFSImpl) GetPrompt(prompt []string) []string { - // adfs only support prompt=login - // ref: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/ad-fs-prompt-login +func (f *ADFSImpl) getPrompt(prompt []string) []string { + // ADFS only supports prompt=login + // https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/ad-fs-prompt-login for _, p := range prompt { if p == "login" { return []string{"login"} diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 3cd2c841be..cc70b59378 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -78,8 +78,11 @@ func (f *AppleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, - Prompt: f.GetPrompt(param.Prompt), - Nonce: param.Nonce, + // Prompt is unset. + // Apple doesn't support prompt parameter + // See "Send the Required Query Parameters" section for supporting parameters + // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms + Nonce: param.Nonce, }), nil } @@ -162,13 +165,6 @@ func (f *AppleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoP return } -func (f *AppleImpl) GetPrompt(prompt []string) []string { - // apple doesn't support prompt parameter - // see "Send the Required Query Parameters" section for supporting parameters - // ref: https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms - return []string{} -} - var ( _ OAuthProvider = &AppleImpl{} ) diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 9f352b26bb..d484ee4cc8 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -50,7 +50,7 @@ func (f *Azureadb2cImpl) GetAuthURL(param GetAuthURLParam) (string, error) { ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, - Prompt: f.GetPrompt(param.Prompt), + Prompt: f.getPrompt(param.Prompt), Nonce: param.Nonce, }), nil } @@ -180,7 +180,7 @@ func (f *Azureadb2cImpl) Extract(claims map[string]interface{}) (stdattrs.T, err }) } -func (f *Azureadb2cImpl) GetPrompt(prompt []string) []string { +func (f *Azureadb2cImpl) getPrompt(prompt []string) []string { // The only supported value is login. // See https://docs.microsoft.com/en-us/azure/active-directory-b2c/openid-connect for _, p := range prompt { diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 93d0beefc2..361e36b0d7 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -83,7 +83,7 @@ func (f *Azureadv2Impl) GetAuthURL(param GetAuthURLParam) (string, error) { ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, - Prompt: f.GetPrompt(param.Prompt), + Prompt: f.getPrompt(param.Prompt), Nonce: param.Nonce, }), nil } @@ -148,12 +148,12 @@ func (f *Azureadv2Impl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthI return } -func (f *Azureadv2Impl) GetPrompt(prompt []string) []string { - // Azureadv2 only support single value for prompt - // the first supporting value in the list will be used - // the usage of `none` is for checking existing authentication and/or consent - // which doesn't fit auth ui case - // ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow +func (f *Azureadv2Impl) getPrompt(prompt []string) []string { + // Azureadv2 only supports single value for prompt. + // The first supporting value in the list will be used. + // The usage of `none` is for checking existing authentication and/or consent + // which doesn't fit auth ui case. + // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow for _, p := range prompt { if p == "login" { return []string{"login"} diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 7075f6035c..1c044d8c97 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -34,8 +34,11 @@ func (f *FacebookImpl) GetAuthURL(param GetAuthURLParam) (string, error) { Scope: f.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset - State: param.State, - Prompt: f.GetPrompt(param.Prompt), + State: param.State, + // Prompt is unset. + // Facebook doesn't support prompt parameter + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/ + // Nonce is unset }.Query()), nil } @@ -129,12 +132,6 @@ func (f *FacebookImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthIn return } -func (f *FacebookImpl) GetPrompt(prompt []string) []string { - // facebook doesn't support prompt parameter - // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/ - return []string{} -} - var ( _ OAuthProvider = &FacebookImpl{} ) diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index dc5802287c..ab6910ddfc 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -164,11 +164,6 @@ func (g *GithubImpl) fetchUserInfo(accessTokenResp oauthrelyingpartyutil.AccessT return } -func (*GithubImpl) GetPrompt(prompt []string) []string { - // Github does not support prompt. - return []string{} -} - var ( _ OAuthProvider = &GithubImpl{} ) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 8b7292401a..64d9b67225 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -34,7 +34,7 @@ func (f *GoogleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { ResponseMode: param.ResponseMode, State: param.State, Nonce: param.Nonce, - Prompt: f.GetPrompt(param.Prompt), + Prompt: f.getPrompt(param.Prompt), }), nil } @@ -115,11 +115,11 @@ func (f *GoogleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfo return } -func (f *GoogleImpl) GetPrompt(prompt []string) []string { - // google support `none`, `concent` and `select_account` for prompt - // the usage of `none` is for checking existing authentication and/or consent - // which doesn't fit auth ui case - // ref: https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters +func (f *GoogleImpl) getPrompt(prompt []string) []string { + // Google supports `none`, `consent` and `select_account` for prompt. + // The usage of `none` is for checking existing authentication and/or consent + // which doesn't fit auth ui case. + // https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters newPrompt := []string{} for _, p := range prompt { if p == "consent" || diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 5d66213d74..289e3bff3c 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -32,8 +32,11 @@ func (f *LinkedInImpl) GetAuthURL(param GetAuthURLParam) (string, error) { Scope: f.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. - State: param.State, - Prompt: f.GetPrompt(param.Prompt), + State: param.State, + // Prompt is unset. + // Linkedin doesn't support prompt parameter + // https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS#step-2-request-an-authorization-code + // Nonce is unset }.Query()), nil } @@ -286,12 +289,6 @@ func (f *LinkedInImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthIn return } -func (f *LinkedInImpl) GetPrompt(prompt []string) []string { - // linkedin doesn't support prompt parameter - // ref: https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS#step-2-request-an-authorization-code - return []string{} -} - func decodeLinkedIn(userInfo map[string]interface{}) (string, stdattrs.T) { profile := userInfo["profile"].(map[string]interface{}) id := profile["id"].(string) diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 0f687d44af..2d4b3fab75 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -38,7 +38,6 @@ type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig GetAuthURL(param GetAuthURLParam) (url string, err error) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) - GetPrompt(prompt []string) []string } type StandardAttributesNormalizer interface { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 4924e7a22d..8bdc3660ea 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -30,8 +30,10 @@ func (w *WechatImpl) GetAuthURL(param GetAuthURLParam) (string, error) { Scope: w.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. - State: param.State, - Prompt: w.GetPrompt(param.Prompt), + State: param.State, + // Prompt is unset. + // Wechat doesn't support prompt parameter + // https://developers.weixin.qq.com/doc/oplatform/en/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html // Nonce is unset. }.Query()), nil } @@ -113,12 +115,6 @@ func (w *WechatImpl) GetAuthInfo(r OAuthAuthorizationResponse, _ GetAuthInfoPara return } -func (w *WechatImpl) GetPrompt(prompt []string) []string { - // wechat doesn't support prompt parameter - // ref: https://developers.weixin.qq.com/doc/oplatform/en/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html - return []string{} -} - var ( _ OAuthProvider = &WechatImpl{} ) From 176752a4b1eabe47df413fb209ed81baa647cd34 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:32:56 +0800 Subject: [PATCH 08/30] Rename GetAuthURL to GetAuthorizationURL --- pkg/lib/authenticationflow/declarative/utils_common.go | 4 ++-- pkg/lib/authn/sso/adfs.go | 2 +- pkg/lib/authn/sso/adfs_test.go | 2 +- pkg/lib/authn/sso/apple.go | 2 +- pkg/lib/authn/sso/apple_test.go | 2 +- pkg/lib/authn/sso/azureadb2c.go | 2 +- pkg/lib/authn/sso/azureadb2c_test.go | 2 +- pkg/lib/authn/sso/azureadv2.go | 2 +- pkg/lib/authn/sso/azureadv2_test.go | 2 +- pkg/lib/authn/sso/facebook.go | 2 +- pkg/lib/authn/sso/facebook_test.go | 2 +- pkg/lib/authn/sso/github.go | 2 +- pkg/lib/authn/sso/github_test.go | 2 +- pkg/lib/authn/sso/google.go | 2 +- pkg/lib/authn/sso/google_test.go | 2 +- pkg/lib/authn/sso/linkedin.go | 2 +- pkg/lib/authn/sso/linkedin_test.go | 2 +- pkg/lib/authn/sso/oauth_provider.go | 4 ++-- pkg/lib/authn/sso/wechat.go | 2 +- pkg/lib/authn/sso/wechat_test.go | 2 +- pkg/lib/interaction/nodes/use_identity_oauth_provider.go | 4 ++-- 21 files changed, 24 insertions(+), 24 deletions(-) diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index f8e9d98790..30331ba17d 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -732,13 +732,13 @@ func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAut uiParam := uiparam.GetUIParam(ctx) - param := sso.GetAuthURLParam{ + param := sso.GetAuthorizationURLOptions{ RedirectURI: opts.RedirectURI, ResponseMode: opts.ResponseMode, Prompt: uiParam.Prompt, } - authorizationURL, err := oauthProvider.GetAuthURL(param) + authorizationURL, err := oauthProvider.GetAuthorizationURL(param) if err != nil { return } diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 33cd4f2e59..258245eac7 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -28,7 +28,7 @@ func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (f *ADFSImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *ADFSImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/authn/sso/adfs_test.go index 9479d2300b..49dbff07ed 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/authn/sso/adfs_test.go @@ -35,7 +35,7 @@ func TestADFSImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index cc70b59378..5482e48541 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -70,7 +70,7 @@ func (f *AppleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *AppleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *AppleImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/authn/sso/apple_test.go index e74bb04e63..0db045f9c2 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/authn/sso/apple_test.go @@ -19,7 +19,7 @@ func TestAppleImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index d484ee4cc8..17c8ba96fd 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -38,7 +38,7 @@ func (f *Azureadb2cImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *Azureadb2cImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *Azureadb2cImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/authn/sso/azureadb2c_test.go index c73d09eded..e9cb3f3cab 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/authn/sso/azureadb2c_test.go @@ -36,7 +36,7 @@ func TestAzureadb2cImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 361e36b0d7..dceba1bffe 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -71,7 +71,7 @@ func (f *Azureadv2Impl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *Azureadv2Impl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *Azureadv2Impl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/authn/sso/azureadv2_test.go index e111bed97e..2d4e348b5d 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/authn/sso/azureadv2_test.go @@ -99,7 +99,7 @@ func TestAzureadv2Impl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 1c044d8c97..99cda38c17 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -27,7 +27,7 @@ func (f *FacebookImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *FacebookImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *FacebookImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/facebook_test.go b/pkg/lib/authn/sso/facebook_test.go index 4743634f06..aef64ee7aa 100644 --- a/pkg/lib/authn/sso/facebook_test.go +++ b/pkg/lib/authn/sso/facebook_test.go @@ -19,7 +19,7 @@ func TestFacebookImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index ab6910ddfc..7f7300e7c5 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -32,7 +32,7 @@ func (g *GithubImpl) Config() oauthrelyingparty.ProviderConfig { return g.ProviderConfig } -func (g *GithubImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (g *GithubImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: g.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/github_test.go b/pkg/lib/authn/sso/github_test.go index b3fd638681..31e8b4925e 100644 --- a/pkg/lib/authn/sso/github_test.go +++ b/pkg/lib/authn/sso/github_test.go @@ -19,7 +19,7 @@ func TestGithubImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 64d9b67225..c1d693c5df 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -21,7 +21,7 @@ type GoogleImpl struct { HTTPClient OAuthHTTPClient } -func (f *GoogleImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *GoogleImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return "", err diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/authn/sso/google_test.go index e45ea1bc98..8ef16e6558 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/authn/sso/google_test.go @@ -89,7 +89,7 @@ func TestGoogleImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 289e3bff3c..d7f135f91c 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -25,7 +25,7 @@ func (f *LinkedInImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *LinkedInImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (f *LinkedInImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/linkedin_test.go b/pkg/lib/authn/sso/linkedin_test.go index d63c1dd104..aa7e97e250 100644 --- a/pkg/lib/authn/sso/linkedin_test.go +++ b/pkg/lib/authn/sso/linkedin_test.go @@ -19,7 +19,7 @@ func TestLinkedInImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 2d4b3fab75..1f2069ea5a 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -16,7 +16,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/clock" ) -type GetAuthURLParam struct { +type GetAuthorizationURLOptions struct { RedirectURI string ResponseMode string Nonce string @@ -36,7 +36,7 @@ type OAuthAuthorizationResponse struct { // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig - GetAuthURL(param GetAuthURLParam) (url string, err error) + GetAuthorizationURL(options GetAuthorizationURLOptions) (url string, err error) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 8bdc3660ea..1bce833ce4 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -22,7 +22,7 @@ func (w *WechatImpl) Config() oauthrelyingparty.ProviderConfig { return w.ProviderConfig } -func (w *WechatImpl) GetAuthURL(param GetAuthURLParam) (string, error) { +func (w *WechatImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. WechatAppID: w.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/wechat_test.go b/pkg/lib/authn/sso/wechat_test.go index 5baa1d7335..28e2f25b63 100644 --- a/pkg/lib/authn/sso/wechat_test.go +++ b/pkg/lib/authn/sso/wechat_test.go @@ -20,7 +20,7 @@ func TestWechatImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthURL(GetAuthURLParam{ + u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index cfdf1b8c38..416daf59a3 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -79,7 +79,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra redirectURIForOAuthProvider = ctx.OAuthRedirectURIBuilder.WeChatCallbackEndpointURL().String() } - param := sso.GetAuthURLParam{ + param := sso.GetAuthorizationURLOptions{ RedirectURI: redirectURIForOAuthProvider, // We use response_mode=form_post if it is supported. ResponseMode: oauthrelyingparty.ResponseModeFormPost, @@ -87,7 +87,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra Prompt: input.GetPrompt(), State: ctx.WebSessionID, } - redirectURI, err := oauthProvider.GetAuthURL(param) + redirectURI, err := oauthProvider.GetAuthorizationURL(param) if err != nil { return nil, err } From 5d550323358ad50484e027e8bcbb7f0db78c5c49 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:46:17 +0800 Subject: [PATCH 09/30] Move GetAuthorizationURLOptions to oauthrelyingparty --- pkg/api/oauthrelyingparty/provider.go | 8 ++++++++ pkg/lib/authenticationflow/declarative/utils_common.go | 3 ++- pkg/lib/authn/sso/adfs.go | 2 +- pkg/lib/authn/sso/adfs_test.go | 2 +- pkg/lib/authn/sso/apple.go | 2 +- pkg/lib/authn/sso/apple_test.go | 2 +- pkg/lib/authn/sso/azureadb2c.go | 2 +- pkg/lib/authn/sso/azureadb2c_test.go | 2 +- pkg/lib/authn/sso/azureadv2.go | 2 +- pkg/lib/authn/sso/azureadv2_test.go | 2 +- pkg/lib/authn/sso/facebook.go | 2 +- pkg/lib/authn/sso/facebook_test.go | 2 +- pkg/lib/authn/sso/github.go | 2 +- pkg/lib/authn/sso/github_test.go | 2 +- pkg/lib/authn/sso/google.go | 2 +- pkg/lib/authn/sso/google_test.go | 2 +- pkg/lib/authn/sso/linkedin.go | 2 +- pkg/lib/authn/sso/linkedin_test.go | 2 +- pkg/lib/authn/sso/oauth_provider.go | 10 +--------- pkg/lib/authn/sso/wechat.go | 2 +- pkg/lib/authn/sso/wechat_test.go | 2 +- .../interaction/nodes/use_identity_oauth_provider.go | 3 +-- 22 files changed, 30 insertions(+), 30 deletions(-) diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go index a39156c4e7..744fc52c4c 100644 --- a/pkg/api/oauthrelyingparty/provider.go +++ b/pkg/api/oauthrelyingparty/provider.go @@ -137,6 +137,14 @@ func (i ProviderID) Equal(that ProviderID) bool { return true } +type GetAuthorizationURLOptions struct { + RedirectURI string + ResponseMode string + Nonce string + State string + Prompt []string +} + type Provider interface { SetDefaults(cfg ProviderConfig) ProviderID(cfg ProviderConfig) ProviderID diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 30331ba17d..d0ab41e053 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -8,6 +8,7 @@ import ( "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/identity" @@ -732,7 +733,7 @@ func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAut uiParam := uiparam.GetUIParam(ctx) - param := sso.GetAuthorizationURLOptions{ + param := oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: opts.RedirectURI, ResponseMode: opts.ResponseMode, Prompt: uiParam.Prompt, diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 258245eac7..f0ceca5769 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -28,7 +28,7 @@ func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (f *ADFSImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/authn/sso/adfs_test.go index 49dbff07ed..eb7a0ca019 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/authn/sso/adfs_test.go @@ -35,7 +35,7 @@ func TestADFSImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 5482e48541..1e03c8ba5d 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -70,7 +70,7 @@ func (f *AppleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *AppleImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/authn/sso/apple_test.go index 0db045f9c2..3fc92f158e 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/authn/sso/apple_test.go @@ -19,7 +19,7 @@ func TestAppleImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 17c8ba96fd..c674f20f54 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -38,7 +38,7 @@ func (f *Azureadb2cImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *Azureadb2cImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/authn/sso/azureadb2c_test.go index e9cb3f3cab..5775c139ec 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/authn/sso/azureadb2c_test.go @@ -36,7 +36,7 @@ func TestAzureadb2cImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index dceba1bffe..a6f79a8307 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -71,7 +71,7 @@ func (f *Azureadv2Impl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *Azureadv2Impl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { return "", err diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/authn/sso/azureadv2_test.go index 2d4e348b5d..f6b5f3f610 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/authn/sso/azureadv2_test.go @@ -99,7 +99,7 @@ func TestAzureadv2Impl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 99cda38c17..6a3aa84b18 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -27,7 +27,7 @@ func (f *FacebookImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *FacebookImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/facebook_test.go b/pkg/lib/authn/sso/facebook_test.go index aef64ee7aa..5798095784 100644 --- a/pkg/lib/authn/sso/facebook_test.go +++ b/pkg/lib/authn/sso/facebook_test.go @@ -19,7 +19,7 @@ func TestFacebookImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 7f7300e7c5..409c2ade1d 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -32,7 +32,7 @@ func (g *GithubImpl) Config() oauthrelyingparty.ProviderConfig { return g.ProviderConfig } -func (g *GithubImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: g.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/github_test.go b/pkg/lib/authn/sso/github_test.go index 31e8b4925e..601fcc34ad 100644 --- a/pkg/lib/authn/sso/github_test.go +++ b/pkg/lib/authn/sso/github_test.go @@ -19,7 +19,7 @@ func TestGithubImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index c1d693c5df..ed91f8885f 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -21,7 +21,7 @@ type GoogleImpl struct { HTTPClient OAuthHTTPClient } -func (f *GoogleImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *GoogleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return "", err diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/authn/sso/google_test.go index 8ef16e6558..b9403494ff 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/authn/sso/google_test.go @@ -89,7 +89,7 @@ func TestGoogleImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index d7f135f91c..d109d55bc9 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -25,7 +25,7 @@ func (f *LinkedInImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *LinkedInImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, diff --git a/pkg/lib/authn/sso/linkedin_test.go b/pkg/lib/authn/sso/linkedin_test.go index aa7e97e250..a3f014d49c 100644 --- a/pkg/lib/authn/sso/linkedin_test.go +++ b/pkg/lib/authn/sso/linkedin_test.go @@ -19,7 +19,7 @@ func TestLinkedInImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 1f2069ea5a..ea2b5f9f19 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -16,14 +16,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/clock" ) -type GetAuthorizationURLOptions struct { - RedirectURI string - ResponseMode string - Nonce string - State string - Prompt []string -} - type GetAuthInfoParam struct { RedirectURI string Nonce string @@ -36,7 +28,7 @@ type OAuthAuthorizationResponse struct { // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig - GetAuthorizationURL(options GetAuthorizationURLOptions) (url string, err error) + GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 1bce833ce4..9ac1e5d27a 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -22,7 +22,7 @@ func (w *WechatImpl) Config() oauthrelyingparty.ProviderConfig { return w.ProviderConfig } -func (w *WechatImpl) GetAuthorizationURL(param GetAuthorizationURLOptions) (string, error) { +func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. WechatAppID: w.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/wechat_test.go b/pkg/lib/authn/sso/wechat_test.go index 28e2f25b63..daf4b9ec90 100644 --- a/pkg/lib/authn/sso/wechat_test.go +++ b/pkg/lib/authn/sso/wechat_test.go @@ -20,7 +20,7 @@ func TestWechatImpl(t *testing.T) { HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ Nonce: "nonce", State: "state", Prompt: []string{"login"}, diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 416daf59a3..7b913c84a8 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -6,7 +6,6 @@ import ( "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" @@ -79,7 +78,7 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra redirectURIForOAuthProvider = ctx.OAuthRedirectURIBuilder.WeChatCallbackEndpointURL().String() } - param := sso.GetAuthorizationURLOptions{ + param := oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: redirectURIForOAuthProvider, // We use response_mode=form_post if it is supported. ResponseMode: oauthrelyingparty.ResponseModeFormPost, From 120cffb73a4fbcbe7f80234d9762b599d6202052 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:55:55 +0800 Subject: [PATCH 10/30] Merge OAuthAuthorizationResponse into GetAuthInfoParam --- pkg/lib/authenticationflow/declarative/utils_common.go | 4 +--- pkg/lib/authn/sso/adfs.go | 4 ++-- pkg/lib/authn/sso/apple.go | 4 ++-- pkg/lib/authn/sso/azureadb2c.go | 4 ++-- pkg/lib/authn/sso/azureadv2.go | 4 ++-- pkg/lib/authn/sso/facebook.go | 4 ++-- pkg/lib/authn/sso/github.go | 8 ++++---- pkg/lib/authn/sso/google.go | 4 ++-- pkg/lib/authn/sso/linkedin.go | 4 ++-- pkg/lib/authn/sso/oauth_provider.go | 7 ++----- pkg/lib/authn/sso/wechat.go | 4 ++-- pkg/lib/interaction/nodes/use_identity_oauth_user_info.go | 4 +--- 12 files changed, 24 insertions(+), 31 deletions(-) diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index d0ab41e053..e2e07e6c6c 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -691,10 +691,8 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA // In the Authentication Flow API, cookies are not sent in Safari in third-party context. emptyNonce := "" authInfo, err := oauthProvider.GetAuthInfo( - sso.OAuthAuthorizationResponse{ - Code: code, - }, sso.GetAuthInfoParam{ + Code: code, RedirectURI: opts.RedirectURI, Nonce: emptyNonce, }, diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index f0ceca5769..c1eb37fb60 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -45,7 +45,7 @@ func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationU }), nil } -func (f *ADFSImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *ADFSImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -61,7 +61,7 @@ func (f *ADFSImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoPa jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, - r.Code, + param.Code, keySet, f.ProviderConfig.ClientID(), f.ClientSecret, diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 1e03c8ba5d..4a8e8d3a1a 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -86,7 +86,7 @@ func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorization }), nil } -func (f *AppleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *AppleImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { keySet, err := appleOIDCConfig.FetchJWKs(f.HTTPClient) if err != nil { return @@ -101,7 +101,7 @@ func (f *AppleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoP jwtToken, err := appleOIDCConfig.ExchangeCode( f.HTTPClient, f.Clock, - r.Code, + param.Code, keySet, f.ProviderConfig.ClientID(), clientSecret, diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index c674f20f54..33d1b4fd5b 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -55,7 +55,7 @@ func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriz }), nil } -func (f *Azureadb2cImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *Azureadb2cImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -70,7 +70,7 @@ func (f *Azureadb2cImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuth jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, - r.Code, + param.Code, keySet, f.ProviderConfig.ClientID(), f.ClientSecret, diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index a6f79a8307..f32bcc75fa 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -88,7 +88,7 @@ func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriza }), nil } -func (f *Azureadv2Impl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *Azureadv2Impl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return @@ -103,7 +103,7 @@ func (f *Azureadv2Impl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthI jwtToken, err := c.ExchangeCode( f.HTTPClient, f.Clock, - r.Code, + param.Code, keySet, f.ProviderConfig.ClientID(), f.ClientSecret, diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 6a3aa84b18..e6bec35f3e 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -43,12 +43,12 @@ func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *FacebookImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *FacebookImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { authInfo = AuthInfo{} accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, - r.Code, + param.Code, facebookTokenURL, param.RedirectURI, f.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 409c2ade1d..00f036fa50 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -46,8 +46,8 @@ func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (g *GithubImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { - accessTokenResp, err := g.exchangeCode(r, param) +func (g *GithubImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { + accessTokenResp, err := g.exchangeCode(param) if err != nil { return } @@ -93,11 +93,11 @@ func (g *GithubImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfo return } -func (g *GithubImpl) exchangeCode(r OAuthAuthorizationResponse, param GetAuthInfoParam) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { +func (g *GithubImpl) exchangeCode(param GetAuthInfoParam) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) q.Set("client_id", g.ProviderConfig.ClientID()) q.Set("client_secret", g.ClientSecret) - q.Set("code", r.Code) + q.Set("code", param.Code) q.Set("redirect_uri", param.RedirectURI) body := strings.NewReader(q.Encode()) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index ed91f8885f..6af063000d 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -42,7 +42,7 @@ func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *GoogleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *GoogleImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return @@ -57,7 +57,7 @@ func (f *GoogleImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfo jwtToken, err := d.ExchangeCode( f.HTTPClient, f.Clock, - r.Code, + param.Code, keySet, f.ProviderConfig.ClientID(), f.ClientSecret, diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index d109d55bc9..3bc1a5940b 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -41,10 +41,10 @@ func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *LinkedInImpl) GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *LinkedInImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, - r.Code, + param.Code, linkedinTokenURL, param.RedirectURI, f.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index ea2b5f9f19..25ff362ba0 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -17,19 +17,16 @@ import ( ) type GetAuthInfoParam struct { + Code string RedirectURI string Nonce string } -type OAuthAuthorizationResponse struct { - Code string -} - // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) - GetAuthInfo(r OAuthAuthorizationResponse, param GetAuthInfoParam) (AuthInfo, error) + GetAuthInfo(param GetAuthInfoParam) (AuthInfo, error) } type StandardAttributesNormalizer interface { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 9ac1e5d27a..0028efceca 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -38,10 +38,10 @@ func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (w *WechatImpl) GetAuthInfo(r OAuthAuthorizationResponse, _ GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (w *WechatImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { accessTokenResp, err := wechatFetchAccessTokenResp( w.HTTPClient, - r.Code, + param.Code, w.ProviderConfig.ClientID(), w.ClientSecret, ) diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index d924b791a3..6ec973b86f 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -74,10 +74,8 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra redirectURI := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias) userInfo, err := oauthProvider.GetAuthInfo( - sso.OAuthAuthorizationResponse{ - Code: code, - }, sso.GetAuthInfoParam{ + Code: code, RedirectURI: redirectURI.String(), Nonce: hashedNonce, }, From 8b11031590b7e2f321cdb51d023debb9ad60cad5 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 14:59:22 +0800 Subject: [PATCH 11/30] Rename GetAuthInfo to GetUserProfile --- pkg/lib/authenticationflow/declarative/utils_common.go | 4 ++-- pkg/lib/authn/sso/adfs.go | 2 +- pkg/lib/authn/sso/apple.go | 2 +- pkg/lib/authn/sso/authinfo.go | 2 +- pkg/lib/authn/sso/azureadb2c.go | 2 +- pkg/lib/authn/sso/azureadv2.go | 2 +- pkg/lib/authn/sso/facebook.go | 4 ++-- pkg/lib/authn/sso/github.go | 4 ++-- pkg/lib/authn/sso/google.go | 2 +- pkg/lib/authn/sso/linkedin.go | 2 +- pkg/lib/authn/sso/oauth_provider.go | 4 ++-- pkg/lib/authn/sso/wechat.go | 2 +- pkg/lib/interaction/nodes/use_identity_oauth_user_info.go | 4 ++-- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index e2e07e6c6c..93aa4ccc9d 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -690,8 +690,8 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA // Nonce in the current implementation is stored in cookies. // In the Authentication Flow API, cookies are not sent in Safari in third-party context. emptyNonce := "" - authInfo, err := oauthProvider.GetAuthInfo( - sso.GetAuthInfoParam{ + authInfo, err := oauthProvider.GetUserProfile( + sso.GetUserProfileOptions{ Code: code, RedirectURI: opts.RedirectURI, Nonce: emptyNonce, diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index c1eb37fb60..ad837a0439 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -45,7 +45,7 @@ func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationU }), nil } -func (f *ADFSImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *ADFSImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 4a8e8d3a1a..8b0db4e8ef 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -86,7 +86,7 @@ func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorization }), nil } -func (f *AppleImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *AppleImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { keySet, err := appleOIDCConfig.FetchJWKs(f.HTTPClient) if err != nil { return diff --git a/pkg/lib/authn/sso/authinfo.go b/pkg/lib/authn/sso/authinfo.go index 42a8a53a50..4f2912c185 100644 --- a/pkg/lib/authn/sso/authinfo.go +++ b/pkg/lib/authn/sso/authinfo.go @@ -4,7 +4,7 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" ) -type AuthInfo struct { +type UserProfile struct { ProviderRawProfile map[string]interface{} // ProviderUserID is not necessarily equal to sub. // If there exists a more unique identifier than sub, that identifier is chosen instead. diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 33d1b4fd5b..5edbf2c6d0 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -55,7 +55,7 @@ func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriz }), nil } -func (f *Azureadb2cImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *Azureadb2cImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index f32bcc75fa..60f80e0864 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -88,7 +88,7 @@ func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriza }), nil } -func (f *Azureadv2Impl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *Azureadv2Impl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index e6bec35f3e..ecd6f7a09b 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -43,8 +43,8 @@ func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *FacebookImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { - authInfo = AuthInfo{} +func (f *FacebookImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { + authInfo = UserProfile{} accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 00f036fa50..de97c85182 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -46,7 +46,7 @@ func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (g *GithubImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (g *GithubImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { accessTokenResp, err := g.exchangeCode(param) if err != nil { return @@ -93,7 +93,7 @@ func (g *GithubImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err return } -func (g *GithubImpl) exchangeCode(param GetAuthInfoParam) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { +func (g *GithubImpl) exchangeCode(param GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) q.Set("client_id", g.ProviderConfig.ClientID()) q.Set("client_secret", g.ClientSecret) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 6af063000d..44a8c34125 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -42,7 +42,7 @@ func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *GoogleImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *GoogleImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 3bc1a5940b..2040f7b85e 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -41,7 +41,7 @@ func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *LinkedInImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (f *LinkedInImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, param.Code, diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 25ff362ba0..5b75de49cb 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -16,7 +16,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/clock" ) -type GetAuthInfoParam struct { +type GetUserProfileOptions struct { Code string RedirectURI string Nonce string @@ -26,7 +26,7 @@ type GetAuthInfoParam struct { type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) - GetAuthInfo(param GetAuthInfoParam) (AuthInfo, error) + GetUserProfile(options GetUserProfileOptions) (UserProfile, error) } type StandardAttributesNormalizer interface { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 0028efceca..1e06f826e8 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -38,7 +38,7 @@ func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (w *WechatImpl) GetAuthInfo(param GetAuthInfoParam) (authInfo AuthInfo, err error) { +func (w *WechatImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { accessTokenResp, err := wechatFetchAccessTokenResp( w.HTTPClient, param.Code, diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 6ec973b86f..0fa48beefe 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -73,8 +73,8 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra redirectURI := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias) - userInfo, err := oauthProvider.GetAuthInfo( - sso.GetAuthInfoParam{ + userInfo, err := oauthProvider.GetUserProfile( + sso.GetUserProfileOptions{ Code: code, RedirectURI: redirectURI.String(), Nonce: hashedNonce, From 20ff1b280d6f1188b8a12ebfafa396b36875f8e4 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:03:20 +0800 Subject: [PATCH 12/30] Avoid using stdattrs.T in UserProfile --- pkg/lib/authenticationflow/declarative/utils_common.go | 2 +- pkg/lib/authn/sso/authinfo.go | 6 +----- pkg/lib/interaction/nodes/use_identity_oauth_user_info.go | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 93aa4ccc9d..95103f41d9 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -709,7 +709,7 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA ProviderID: providerID, SubjectID: authInfo.ProviderUserID, RawProfile: authInfo.ProviderRawProfile, - StandardClaims: authInfo.StandardAttributes.ToClaims(), + StandardClaims: authInfo.StandardAttributes, }, } diff --git a/pkg/lib/authn/sso/authinfo.go b/pkg/lib/authn/sso/authinfo.go index 4f2912c185..90df4cdac8 100644 --- a/pkg/lib/authn/sso/authinfo.go +++ b/pkg/lib/authn/sso/authinfo.go @@ -1,13 +1,9 @@ package sso -import ( - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" -) - type UserProfile struct { ProviderRawProfile map[string]interface{} // ProviderUserID is not necessarily equal to sub. // If there exists a more unique identifier than sub, that identifier is chosen instead. ProviderUserID string - StandardAttributes stdattrs.T + StandardAttributes map[string]interface{} } diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 0fa48beefe..8c41975cc3 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -92,7 +92,7 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra ProviderID: providerID, SubjectID: userInfo.ProviderUserID, RawProfile: userInfo.ProviderRawProfile, - StandardClaims: userInfo.StandardAttributes.ToClaims(), + StandardClaims: userInfo.StandardAttributes, }, } From edef218c03ff72607e08b2d5e555a6a5180b324c Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:09:23 +0800 Subject: [PATCH 13/30] Call NewOAuthProvider as late as possible --- .../interaction/nodes/use_identity_oauth_user_info.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 8c41975cc3..4fc83204fc 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -53,11 +53,6 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra return nil, fmt.Errorf("interaction: unexpected provider alias %s != %s", providerConfigAlias, alias) } - oauthProvider := ctx.OAuthProviderFactory.NewOAuthProvider(alias) - if oauthProvider == nil { - return nil, api.ErrOAuthProviderNotFound - } - // Handle provider error if oauthError != "" { return nil, oauthrelyingpartyutil.NewOAuthError(oauthError, errorDescription, errorURI) @@ -66,11 +61,17 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra if nonceSource == "" { return nil, fmt.Errorf("nonce does not present in the request") } + nonce := crypto.SHA256String(nonceSource) if subtle.ConstantTimeCompare([]byte(hashedNonce), []byte(nonce)) != 1 { return nil, fmt.Errorf("invalid nonce") } + oauthProvider := ctx.OAuthProviderFactory.NewOAuthProvider(alias) + if oauthProvider == nil { + return nil, api.ErrOAuthProviderNotFound + } + redirectURI := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias) userInfo, err := oauthProvider.GetUserProfile( From 44cab8c9083ef27ffa777b9e09a2586b37d05845 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:22:14 +0800 Subject: [PATCH 14/30] Move GetUserProfileOptions and UserProfile to oauthrelyingparty --- pkg/api/oauthrelyingparty/provider.go | 14 ++++++++++++++ .../authenticationflow/declarative/utils_common.go | 3 +-- pkg/lib/authn/sso/adfs.go | 2 +- pkg/lib/authn/sso/apple.go | 2 +- pkg/lib/authn/sso/authinfo.go | 9 --------- pkg/lib/authn/sso/azureadb2c.go | 2 +- pkg/lib/authn/sso/azureadv2.go | 2 +- pkg/lib/authn/sso/facebook.go | 4 ++-- pkg/lib/authn/sso/github.go | 4 ++-- pkg/lib/authn/sso/google.go | 2 +- pkg/lib/authn/sso/linkedin.go | 2 +- pkg/lib/authn/sso/oauth_provider.go | 8 +------- pkg/lib/authn/sso/wechat.go | 2 +- .../nodes/use_identity_oauth_user_info.go | 3 +-- 14 files changed, 28 insertions(+), 31 deletions(-) delete mode 100644 pkg/lib/authn/sso/authinfo.go diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go index 744fc52c4c..cc4673e887 100644 --- a/pkg/api/oauthrelyingparty/provider.go +++ b/pkg/api/oauthrelyingparty/provider.go @@ -145,6 +145,20 @@ type GetAuthorizationURLOptions struct { Prompt []string } +type GetUserProfileOptions struct { + Code string + RedirectURI string + Nonce string +} + +type UserProfile struct { + ProviderRawProfile map[string]interface{} + // ProviderUserID is not necessarily equal to sub. + // If there exists a more unique identifier than sub, that identifier is chosen instead. + ProviderUserID string + StandardAttributes map[string]interface{} +} + type Provider interface { SetDefaults(cfg ProviderConfig) ProviderID(cfg ProviderConfig) ProviderID diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 95103f41d9..92bd5c3a35 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -13,7 +13,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/otp" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/mail" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" @@ -691,7 +690,7 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA // In the Authentication Flow API, cookies are not sent in Safari in third-party context. emptyNonce := "" authInfo, err := oauthProvider.GetUserProfile( - sso.GetUserProfileOptions{ + oauthrelyingparty.GetUserProfileOptions{ Code: code, RedirectURI: opts.RedirectURI, Nonce: emptyNonce, diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index ad837a0439..e1db24f91c 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -45,7 +45,7 @@ func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationU }), nil } -func (f *ADFSImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *ADFSImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 8b0db4e8ef..f6747670c8 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -86,7 +86,7 @@ func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorization }), nil } -func (f *AppleImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *AppleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { keySet, err := appleOIDCConfig.FetchJWKs(f.HTTPClient) if err != nil { return diff --git a/pkg/lib/authn/sso/authinfo.go b/pkg/lib/authn/sso/authinfo.go deleted file mode 100644 index 90df4cdac8..0000000000 --- a/pkg/lib/authn/sso/authinfo.go +++ /dev/null @@ -1,9 +0,0 @@ -package sso - -type UserProfile struct { - ProviderRawProfile map[string]interface{} - // ProviderUserID is not necessarily equal to sub. - // If there exists a more unique identifier than sub, that identifier is chosen instead. - ProviderUserID string - StandardAttributes map[string]interface{} -} diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 5edbf2c6d0..9ace50d275 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -55,7 +55,7 @@ func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriz }), nil } -func (f *Azureadb2cImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *Azureadb2cImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 60f80e0864..a40853e71b 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -88,7 +88,7 @@ func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriza }), nil } -func (f *Azureadv2Impl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *Azureadv2Impl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { c, err := f.getOpenIDConfiguration() if err != nil { return diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index ecd6f7a09b..17d9851bd5 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -43,8 +43,8 @@ func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *FacebookImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { - authInfo = UserProfile{} +func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + authInfo = oauthrelyingparty.UserProfile{} accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index de97c85182..a2f69f42c3 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -46,7 +46,7 @@ func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (g *GithubImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { accessTokenResp, err := g.exchangeCode(param) if err != nil { return @@ -93,7 +93,7 @@ func (g *GithubImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserP return } -func (g *GithubImpl) exchangeCode(param GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { +func (g *GithubImpl) exchangeCode(param oauthrelyingparty.GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) q.Set("client_id", g.ProviderConfig.ClientID()) q.Set("client_secret", g.ClientSecret) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 44a8c34125..3c59f71d3d 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -42,7 +42,7 @@ func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { return f.ProviderConfig } -func (f *GoogleImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *GoogleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 2040f7b85e..575df087ae 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -41,7 +41,7 @@ func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *LinkedInImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (f *LinkedInImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( f.HTTPClient.Client, param.Code, diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 5b75de49cb..5e0e03c1a5 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -16,17 +16,11 @@ import ( "github.com/authgear/authgear-server/pkg/util/clock" ) -type GetUserProfileOptions struct { - Code string - RedirectURI string - Nonce string -} - // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { Config() oauthrelyingparty.ProviderConfig GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) - GetUserProfile(options GetUserProfileOptions) (UserProfile, error) + GetUserProfile(options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) } type StandardAttributesNormalizer interface { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 1e06f826e8..ee7ed17ed6 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -38,7 +38,7 @@ func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (w *WechatImpl) GetUserProfile(param GetUserProfileOptions) (authInfo UserProfile, err error) { +func (w *WechatImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { accessTokenResp, err := wechatFetchAccessTokenResp( w.HTTPClient, param.Code, diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 4fc83204fc..92936d3635 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -8,7 +8,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" @@ -75,7 +74,7 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra redirectURI := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias) userInfo, err := oauthProvider.GetUserProfile( - sso.GetUserProfileOptions{ + oauthrelyingparty.GetUserProfileOptions{ Code: code, RedirectURI: redirectURI.String(), Nonce: hashedNonce, From 555612eb6c393bac43a75ce63a68553e47df8931 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:45:53 +0800 Subject: [PATCH 15/30] Change OAuthProviderFactory to work like a provider --- .../declarative/utils_common.go | 25 +++--- pkg/lib/authenticationflow/dependencies.go | 6 +- pkg/lib/authn/sso/oauth_provider.go | 76 +++++++++++++------ pkg/lib/config/identity.go | 4 +- pkg/lib/interaction/context.go | 6 +- .../nodes/use_identity_oauth_provider.go | 12 +-- .../nodes/use_identity_oauth_user_info.go | 15 ++-- 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 92bd5c3a35..cf6407db62 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -678,18 +678,19 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA return nil, oauthrelyingpartyutil.NewOAuthError(oauthError, errorDescription, errorURI) } - oauthProvider := deps.OAuthProviderFactory.NewOAuthProvider(opts.Alias) - if oauthProvider == nil { - return nil, api.ErrOAuthProviderNotFound - } - code := inputOAuth.GetOAuthAuthorizationCode() + providerConfig, err := deps.OAuthProviderFactory.GetProviderConfig(opts.Alias) + if err != nil { + return nil, err + } + // TODO(authflow): support nonce but do not save nonce in cookies. // Nonce in the current implementation is stored in cookies. // In the Authentication Flow API, cookies are not sent in Safari in third-party context. emptyNonce := "" - authInfo, err := oauthProvider.GetUserProfile( + authInfo, err := deps.OAuthProviderFactory.GetUserProfile( + opts.Alias, oauthrelyingparty.GetUserProfileOptions{ Code: code, RedirectURI: opts.RedirectURI, @@ -700,7 +701,6 @@ func handleOAuthAuthorizationResponse(deps *authflow.Dependencies, opts HandleOA return nil, err } - providerConfig := oauthProvider.Config() providerID := providerConfig.ProviderID() identitySpec := &identity.Spec{ Type: model.IdentityTypeOAuth, @@ -722,9 +722,8 @@ type GetOAuthDataOptions struct { } func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAuthDataOptions) (data OAuthData, err error) { - oauthProvider := deps.OAuthProviderFactory.NewOAuthProvider(opts.Alias) - if oauthProvider == nil { - err = api.ErrOAuthProviderNotFound + providerConfig, err := deps.OAuthProviderFactory.GetProviderConfig(opts.Alias) + if err != nil { return } @@ -736,16 +735,16 @@ func getOAuthData(ctx context.Context, deps *authflow.Dependencies, opts GetOAut Prompt: uiParam.Prompt, } - authorizationURL, err := oauthProvider.GetAuthorizationURL(param) + authorizationURL, err := deps.OAuthProviderFactory.GetAuthorizationURL(opts.Alias, param) if err != nil { return } data = NewOAuthData(OAuthData{ Alias: opts.Alias, - OAuthProviderType: oauthProvider.Config().Type(), + OAuthProviderType: providerConfig.Type(), OAuthAuthorizationURL: authorizationURL, - WechatAppType: wechat.ProviderConfig(oauthProvider.Config()).AppType(), + WechatAppType: wechat.ProviderConfig(providerConfig).AppType(), }) return } diff --git a/pkg/lib/authenticationflow/dependencies.go b/pkg/lib/authenticationflow/dependencies.go index 63281eef30..c028388315 100644 --- a/pkg/lib/authenticationflow/dependencies.go +++ b/pkg/lib/authenticationflow/dependencies.go @@ -9,6 +9,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/accountmigration" "github.com/authgear/authgear-server/pkg/lib/authn/attrs" "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" @@ -19,7 +20,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/identity/anonymous" "github.com/authgear/authgear-server/pkg/lib/authn/mfa" "github.com/authgear/authgear-server/pkg/lib/authn/otp" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/authn/user" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/facade" @@ -190,7 +190,9 @@ type OfflineGrantStore interface { } type OAuthProviderFactory interface { - NewOAuthProvider(alias string) sso.OAuthProvider + GetProviderConfig(alias string) (oauthrelyingparty.ProviderConfig, error) + GetAuthorizationURL(alias string, options oauthrelyingparty.GetAuthorizationURLOptions) (string, error) + GetUserProfile(alias string, options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) } type PasskeyRequestOptionsService interface { diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 5e0e03c1a5..44e675277b 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -1,6 +1,7 @@ package sso import ( + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" @@ -35,85 +36,116 @@ type OAuthProviderFactory struct { HTTPClient OAuthHTTPClient } -func (p *OAuthProviderFactory) NewOAuthProvider(alias string) OAuthProvider { +func (p *OAuthProviderFactory) GetProviderConfig(alias string) (oauthrelyingparty.ProviderConfig, error) { providerConfig, ok := p.IdentityConfig.OAuth.GetProviderConfig(alias) if !ok { - return nil + return nil, api.ErrOAuthProviderNotFound } + return providerConfig, nil +} + +func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider, err error) { + providerConfig, err := p.GetProviderConfig(alias) + if err != nil { + return + } + credentials, ok := p.Credentials.Lookup(alias) if !ok { - return nil + err = api.ErrOAuthProviderNotFound + return } switch providerConfig.Type() { case google.Type: return &GoogleImpl{ Clock: p.Clock, - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case facebook.Type: return &FacebookImpl{ - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case github.Type: return &GithubImpl{ - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case linkedin.Type: return &LinkedInImpl{ - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case azureadv2.Type: return &Azureadv2Impl{ Clock: p.Clock, - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case azureadb2c.Type: return &Azureadb2cImpl{ Clock: p.Clock, - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case adfs.Type: return &ADFSImpl{ Clock: p.Clock, - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case apple.Type: return &AppleImpl{ Clock: p.Clock, - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil case wechat.Type: return &WechatImpl{ - ProviderConfig: *providerConfig, + ProviderConfig: providerConfig, ClientSecret: credentials.ClientSecret, StandardAttributesNormalizer: p.StandardAttributesNormalizer, HTTPClient: p.HTTPClient, - } + }, nil + default: + // TODO(oauth): switch to registry-based resolution. + err = api.ErrOAuthProviderNotFound + return + } +} + +func (p *OAuthProviderFactory) GetAuthorizationURL(alias string, options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) { + provider, err := p.getProvider(alias) + if err != nil { + return + } + + return provider.GetAuthorizationURL(options) +} + +func (p *OAuthProviderFactory) GetUserProfile(alias string, options oauthrelyingparty.GetUserProfileOptions) (userProfile oauthrelyingparty.UserProfile, err error) { + provider, err := p.getProvider(alias) + if err != nil { + return } - return nil + + return provider.GetUserProfile(options) } diff --git a/pkg/lib/config/identity.go b/pkg/lib/config/identity.go index 1bf293fd51..f9bbb35efd 100644 --- a/pkg/lib/config/identity.go +++ b/pkg/lib/config/identity.go @@ -257,11 +257,11 @@ type OAuthSSOConfig struct { Providers []oauthrelyingparty.ProviderConfig `json:"providers,omitempty"` } -func (c *OAuthSSOConfig) GetProviderConfig(alias string) (*oauthrelyingparty.ProviderConfig, bool) { +func (c *OAuthSSOConfig) GetProviderConfig(alias string) (oauthrelyingparty.ProviderConfig, bool) { for _, conf := range c.Providers { if conf.Alias() == alias { cc := conf - return &cc, true + return cc, true } } return nil, false diff --git a/pkg/lib/interaction/context.go b/pkg/lib/interaction/context.go index 9acb8c97ae..476fcb9c69 100644 --- a/pkg/lib/interaction/context.go +++ b/pkg/lib/interaction/context.go @@ -7,6 +7,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator/service" @@ -16,7 +17,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/identity/biometric" "github.com/authgear/authgear-server/pkg/lib/authn/mfa" "github.com/authgear/authgear-server/pkg/lib/authn/otp" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/authn/user" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/facade" @@ -138,7 +138,9 @@ type SessionManager interface { } type OAuthProviderFactory interface { - NewOAuthProvider(alias string) sso.OAuthProvider + GetProviderConfig(alias string) (oauthrelyingparty.ProviderConfig, error) + GetAuthorizationURL(alias string, options oauthrelyingparty.GetAuthorizationURLOptions) (string, error) + GetUserProfile(alias string, options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) } type OAuthRedirectURIBuilder interface { diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 7b913c84a8..0d5f2ef7b6 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -65,16 +65,16 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra nonceSource := ctx.Nonces.GenerateAndSet() errorRedirectURI := input.GetErrorRedirectURI() - oauthProvider := ctx.OAuthProviderFactory.NewOAuthProvider(alias) - if oauthProvider == nil { - return nil, api.ErrOAuthProviderNotFound + providerConfig, err := ctx.OAuthProviderFactory.GetProviderConfig(alias) + if err != nil { + return nil, err } nonce := crypto.SHA256String(nonceSource) redirectURIForOAuthProvider := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias).String() // Special case: wechat needs to use a special callback endpoint. - if oauthProvider.Config().Type() == wechat.Type { + if providerConfig.Type() == wechat.Type { redirectURIForOAuthProvider = ctx.OAuthRedirectURIBuilder.WeChatCallbackEndpointURL().String() } @@ -86,13 +86,13 @@ func (e *EdgeUseIdentityOAuthProvider) Instantiate(ctx *interaction.Context, gra Prompt: input.GetPrompt(), State: ctx.WebSessionID, } - redirectURI, err := oauthProvider.GetAuthorizationURL(param) + redirectURI, err := ctx.OAuthProviderFactory.GetAuthorizationURL(alias, param) if err != nil { return nil, err } // Special case: wechat needs to redirect a special page. - if oauthProvider.Config().Type() == wechat.Type { + if providerConfig.Type() == wechat.Type { v := url.Values{} v.Add("x_auth_url", redirectURI) redirectURI = ctx.OAuthRedirectURIBuilder.WeChatAuthorizeURL(alias).String() + "?" + v.Encode() diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 92936d3635..02024068a1 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -4,7 +4,6 @@ import ( "crypto/subtle" "fmt" - "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" @@ -66,14 +65,15 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra return nil, fmt.Errorf("invalid nonce") } - oauthProvider := ctx.OAuthProviderFactory.NewOAuthProvider(alias) - if oauthProvider == nil { - return nil, api.ErrOAuthProviderNotFound - } - redirectURI := ctx.OAuthRedirectURIBuilder.SSOCallbackURL(alias) - userInfo, err := oauthProvider.GetUserProfile( + providerConfig, err := ctx.OAuthProviderFactory.GetProviderConfig(alias) + if err != nil { + return nil, err + } + + userInfo, err := ctx.OAuthProviderFactory.GetUserProfile( + alias, oauthrelyingparty.GetUserProfileOptions{ Code: code, RedirectURI: redirectURI.String(), @@ -84,7 +84,6 @@ func (e *EdgeUseIdentityOAuthUserInfo) Instantiate(ctx *interaction.Context, gra return nil, err } - providerConfig := oauthProvider.Config() providerID := providerConfig.ProviderID() spec := &identity.Spec{ Type: model.IdentityTypeOAuth, From ca5c558c9459a0fb43c384f67c309ac8a9f76c35 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:50:04 +0800 Subject: [PATCH 16/30] Remove Config() from OAuthProvider --- pkg/lib/authn/sso/adfs.go | 4 ---- pkg/lib/authn/sso/apple.go | 4 ---- pkg/lib/authn/sso/azureadb2c.go | 4 ---- pkg/lib/authn/sso/azureadv2.go | 4 ---- pkg/lib/authn/sso/facebook.go | 4 ---- pkg/lib/authn/sso/github.go | 4 ---- pkg/lib/authn/sso/google.go | 4 ---- pkg/lib/authn/sso/linkedin.go | 4 ---- pkg/lib/authn/sso/oauth_provider.go | 1 - pkg/lib/authn/sso/wechat.go | 4 ---- 10 files changed, 37 deletions(-) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index e1db24f91c..2ddd003b11 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -19,10 +19,6 @@ type ADFSImpl struct { HTTPClient OAuthHTTPClient } -func (f *ADFSImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { endpoint := adfs.ProviderConfig(f.ProviderConfig).DiscoveryDocumentEndpoint() return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index f6747670c8..0b7ed6ac37 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -66,10 +66,6 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { return } -func (f *AppleImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 9ace50d275..fbc5a5773d 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -34,10 +34,6 @@ func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (f *Azureadb2cImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index a40853e71b..6a9a9fc0e3 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -67,10 +67,6 @@ func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) } -func (f *Azureadv2Impl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { c, err := f.getOpenIDConfiguration() if err != nil { diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index 17d9851bd5..ed3c498e6e 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -23,10 +23,6 @@ type FacebookImpl struct { HTTPClient OAuthHTTPClient } -func (f *FacebookImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index a2f69f42c3..3e439ddcff 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -28,10 +28,6 @@ type GithubImpl struct { HTTPClient OAuthHTTPClient } -func (g *GithubImpl) Config() oauthrelyingparty.ProviderConfig { - return g.ProviderConfig -} - func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 3c59f71d3d..7bd318fa9e 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -38,10 +38,6 @@ func (f *GoogleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }), nil } -func (f *GoogleImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *GoogleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index 575df087ae..d9b0fb0b4d 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -21,10 +21,6 @@ type LinkedInImpl struct { HTTPClient OAuthHTTPClient } -func (f *LinkedInImpl) Config() oauthrelyingparty.ProviderConfig { - return f.ProviderConfig -} - func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: f.ProviderConfig.ClientID(), diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 44e675277b..c87f125e68 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -19,7 +19,6 @@ import ( // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { - Config() oauthrelyingparty.ProviderConfig GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) GetUserProfile(options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index ee7ed17ed6..1e96dd236d 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -18,10 +18,6 @@ type WechatImpl struct { HTTPClient OAuthHTTPClient } -func (w *WechatImpl) Config() oauthrelyingparty.ProviderConfig { - return w.ProviderConfig -} - func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. From 26468832efa02ca1d2fb2fcc541158e193a130e3 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 15:59:41 +0800 Subject: [PATCH 17/30] Move standard attributes normalization out of OAuthProvider --- pkg/lib/authn/sso/adfs.go | 14 ++--- pkg/lib/authn/sso/apple.go | 14 ++--- pkg/lib/authn/sso/azureadb2c.go | 14 ++--- pkg/lib/authn/sso/azureadv2.go | 14 ++--- pkg/lib/authn/sso/facebook.go | 12 +--- pkg/lib/authn/sso/github.go | 12 +--- pkg/lib/authn/sso/google.go | 14 ++--- pkg/lib/authn/sso/linkedin.go | 12 +--- pkg/lib/authn/sso/oauth_provider.go | 85 +++++++++++++++-------------- pkg/lib/authn/sso/wechat.go | 12 +--- 10 files changed, 75 insertions(+), 128 deletions(-) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 2ddd003b11..4e5633e66f 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -12,11 +12,10 @@ import ( ) type ADFSImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + Clock clock.Clock + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { @@ -117,11 +116,6 @@ func (f *ADFSImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = sub - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 0b7ed6ac37..2458fe5e4f 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -25,11 +25,10 @@ var appleOIDCConfig = OIDCDiscoveryDocument{ } type AppleImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + Clock clock.Clock + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { @@ -153,11 +152,6 @@ func (f *AppleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions } authInfo.StandardAttributes = stdAttrs.WithNameCopiedToGivenName() - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index fbc5a5773d..7ab1ba7e6a 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -12,11 +12,10 @@ import ( ) type Azureadb2cImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + Clock clock.Clock + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { @@ -110,11 +109,6 @@ func (f *Azureadb2cImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOp } authInfo.StandardAttributes = stdAttrs - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index 6a9a9fc0e3..a9391d44b9 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -12,11 +12,10 @@ import ( ) type Azureadv2Impl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + Clock clock.Clock + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { @@ -136,11 +135,6 @@ func (f *Azureadv2Impl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpt } authInfo.StandardAttributes = stdAttrs - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index ed3c498e6e..ddf6343bb8 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -17,10 +17,9 @@ const ( ) type FacebookImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { @@ -120,11 +119,6 @@ func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti } authInfo.StandardAttributes = stdAttrs - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index 3e439ddcff..bf3e8d4431 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -22,10 +22,9 @@ const ( ) type GithubImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { @@ -81,11 +80,6 @@ func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption } authInfo.StandardAttributes = stdAttrs - err = g.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 7bd318fa9e..69aba3ac5c 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -14,11 +14,10 @@ const ( ) type GoogleImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + Clock clock.Clock + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *GoogleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { @@ -103,11 +102,6 @@ func (f *GoogleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption } authInfo.StandardAttributes = stdAttrs - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index d9b0fb0b4d..f7c908d6e2 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -15,10 +15,9 @@ const ( ) type LinkedInImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { @@ -277,11 +276,6 @@ func (f *LinkedInImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti } authInfo.StandardAttributes = attrs - err = f.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index c87f125e68..c6f1860e2c 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -58,71 +58,62 @@ func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider switch providerConfig.Type() { case google.Type: return &GoogleImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case facebook.Type: return &FacebookImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case github.Type: return &GithubImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case linkedin.Type: return &LinkedInImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case azureadv2.Type: return &Azureadv2Impl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case azureadb2c.Type: return &Azureadb2cImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case adfs.Type: return &ADFSImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case apple.Type: return &AppleImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil case wechat.Type: return &WechatImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - StandardAttributesNormalizer: p.StandardAttributesNormalizer, - HTTPClient: p.HTTPClient, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient, }, nil default: // TODO(oauth): switch to registry-based resolution. @@ -146,5 +137,15 @@ func (p *OAuthProviderFactory) GetUserProfile(alias string, options oauthrelying return } - return provider.GetUserProfile(options) + userProfile, err = provider.GetUserProfile(options) + if err != nil { + return + } + + err = p.StandardAttributesNormalizer.Normalize(userProfile.StandardAttributes) + if err != nil { + return + } + + return } diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 1e96dd236d..358cb3867f 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -12,10 +12,9 @@ const ( ) type WechatImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - StandardAttributesNormalizer StandardAttributesNormalizer - HTTPClient OAuthHTTPClient + ProviderConfig oauthrelyingparty.ProviderConfig + ClientSecret string + HTTPClient OAuthHTTPClient } func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { @@ -103,11 +102,6 @@ func (w *WechatImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption stdattrs.Gender: gender, }.WithNameCopiedToGivenName() - err = w.StandardAttributesNormalizer.Normalize(authInfo.StandardAttributes) - if err != nil { - return - } - return } From b20bbc7922e1ec6facae11218fa779767c7f5203 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 16:47:40 +0800 Subject: [PATCH 18/30] Remove all fields from implementation structs --- pkg/api/oauthrelyingparty/provider.go | 13 ++++ pkg/lib/authn/sso/adfs.go | 38 +++++------- pkg/lib/authn/sso/adfs_test.go | 10 +-- pkg/lib/authn/sso/apple.go | 40 +++++------- pkg/lib/authn/sso/apple_test.go | 6 +- pkg/lib/authn/sso/azureadb2c.go | 42 ++++++------- pkg/lib/authn/sso/azureadb2c_test.go | 10 +-- pkg/lib/authn/sso/azureadv2.go | 38 +++++------- pkg/lib/authn/sso/azureadv2_test.go | 10 +-- pkg/lib/authn/sso/facebook.go | 26 ++++---- pkg/lib/authn/sso/facebook_test.go | 6 +- pkg/lib/authn/sso/github.go | 34 +++++----- pkg/lib/authn/sso/github_test.go | 6 +- pkg/lib/authn/sso/google.go | 32 ++++------ pkg/lib/authn/sso/google_test.go | 10 +-- pkg/lib/authn/sso/linkedin.go | 26 ++++---- pkg/lib/authn/sso/linkedin_test.go | 6 +- pkg/lib/authn/sso/oauth_provider.go | 89 ++++++++++----------------- pkg/lib/authn/sso/oidc.go | 11 ++-- pkg/lib/authn/sso/wechat.go | 24 +++----- pkg/lib/authn/sso/wechat_authinfo.go | 5 +- pkg/lib/authn/sso/wechat_test.go | 8 +-- 22 files changed, 220 insertions(+), 270 deletions(-) diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go index cc4673e887..92f68051a6 100644 --- a/pkg/api/oauthrelyingparty/provider.go +++ b/pkg/api/oauthrelyingparty/provider.go @@ -2,6 +2,8 @@ package oauthrelyingparty import ( "fmt" + "net/http" + "time" ) type ProviderConfig map[string]interface{} @@ -159,6 +161,17 @@ type UserProfile struct { StandardAttributes map[string]interface{} } +type Clock interface { + NowUTC() time.Time +} + +type Dependencies struct { + Clock Clock + ProviderConfig ProviderConfig + ClientSecret string + HTTPClient *http.Client +} + type Provider interface { SetDefaults(cfg ProviderConfig) ProviderID(cfg ProviderConfig) ProviderID diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index 4e5633e66f..ece7a26b7d 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -7,31 +7,25 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/validation" ) -type ADFSImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type ADFSImpl struct{} -func (f *ADFSImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { - endpoint := adfs.ProviderConfig(f.ProviderConfig).DiscoveryDocumentEndpoint() - return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) +func (f *ADFSImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { + endpoint := adfs.ProviderConfig(deps.ProviderConfig).DiscoveryDocumentEndpoint() + return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } -func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration() +func (f *ADFSImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return "", err } return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -40,26 +34,26 @@ func (f *ADFSImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationU }), nil } -func (f *ADFSImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration() +func (f *ADFSImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return } // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(f.HTTPClient) + keySet, err := c.FetchJWKs(deps.HTTPClient) if err != nil { return } var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( - f.HTTPClient, - f.Clock, + deps.HTTPClient, + deps.Clock, param.Code, keySet, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, @@ -104,7 +98,7 @@ func (f *ADFSImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) } } - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() extracted, err = stdattrs.Extract(extracted, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/authn/sso/adfs_test.go index eb7a0ca019..9194aa6648 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/authn/sso/adfs_test.go @@ -13,11 +13,11 @@ import ( func TestADFSImpl(t *testing.T) { Convey("ADFSImpl", t, func() { - client := OAuthHTTPClient{&http.Client{}} - gock.InterceptClient(client.Client) + client := &http.Client{} + gock.InterceptClient(client) defer gock.Off() - g := &ADFSImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": adfs.Type, @@ -26,6 +26,8 @@ func TestADFSImpl(t *testing.T) { HTTPClient: client, } + g := &ADFSImpl{} + gock.New("https://localhost/.well-known/openid-configuration"). Reply(200). BodyString(` @@ -35,7 +37,7 @@ func TestADFSImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 2458fe5e4f..6b74559dc8 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -12,7 +12,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwtutil" @@ -24,31 +23,26 @@ var appleOIDCConfig = OIDCDiscoveryDocument{ AuthorizationEndpoint: "https://appleid.apple.com/auth/authorize", } -type AppleImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type AppleImpl struct{} -func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { - teamID := apple.ProviderConfig(f.ProviderConfig).TeamID() - keyID := apple.ProviderConfig(f.ProviderConfig).KeyID() +func (f *AppleImpl) createClientSecret(deps oauthrelyingparty.Dependencies) (clientSecret string, err error) { + teamID := apple.ProviderConfig(deps.ProviderConfig).TeamID() + keyID := apple.ProviderConfig(deps.ProviderConfig).KeyID() // https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens - key, err := crypto.ParseAppleP8PrivateKey([]byte(f.ClientSecret)) + key, err := crypto.ParseAppleP8PrivateKey([]byte(deps.ClientSecret)) if err != nil { return } - now := f.Clock.NowUTC() + now := deps.Clock.NowUTC() payload := jwt.New() _ = payload.Set(jwt.IssuerKey, teamID) _ = payload.Set(jwt.IssuedAtKey, now.Unix()) _ = payload.Set(jwt.ExpirationKey, now.Add(duration.Short).Unix()) _ = payload.Set(jwt.AudienceKey, "https://appleid.apple.com") - _ = payload.Set(jwt.SubjectKey, f.ProviderConfig.ClientID) + _ = payload.Set(jwt.SubjectKey, deps.ProviderConfig.ClientID) jwkKey, err := jwk.FromRaw(key) if err != nil { @@ -65,11 +59,11 @@ func (f *AppleImpl) createClientSecret() (clientSecret string, err error) { return } -func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (f *AppleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -81,24 +75,24 @@ func (f *AppleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorization }), nil } -func (f *AppleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - keySet, err := appleOIDCConfig.FetchJWKs(f.HTTPClient) +func (f *AppleImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + keySet, err := appleOIDCConfig.FetchJWKs(deps.HTTPClient) if err != nil { return } - clientSecret, err := f.createClientSecret() + clientSecret, err := f.createClientSecret(deps) if err != nil { return } var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := appleOIDCConfig.ExchangeCode( - f.HTTPClient, - f.Clock, + deps.HTTPClient, + deps.Clock, param.Code, keySet, - f.ProviderConfig.ClientID(), + deps.ProviderConfig.ClientID(), clientSecret, param.RedirectURI, param.Nonce, @@ -143,7 +137,7 @@ func (f *AppleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = sub - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/authn/sso/apple_test.go index 3fc92f158e..38955fa534 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/authn/sso/apple_test.go @@ -11,15 +11,15 @@ import ( func TestAppleImpl(t *testing.T) { Convey("AppleImpl", t, func() { - g := &AppleImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": apple.Type, }, - HTTPClient: OAuthHTTPClient{}, } + g := &AppleImpl{} - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 7ab1ba7e6a..3fe7ee62fa 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -8,18 +8,12 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" ) -type Azureadb2cImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type Azureadb2cImpl struct{} -func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { - azureadb2cConfig := azureadb2c.ProviderConfig(f.ProviderConfig) +func (f *Azureadb2cImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { + azureadb2cConfig := azureadb2c.ProviderConfig(deps.ProviderConfig) tenant := azureadb2cConfig.Tenant() policy := azureadb2cConfig.Policy() @@ -30,18 +24,18 @@ func (f *Azureadb2cImpl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error policy, ) - return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) + return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } -func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration() +func (f *Azureadb2cImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return "", err } return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -50,25 +44,25 @@ func (f *Azureadb2cImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriz }), nil } -func (f *Azureadb2cImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration() +func (f *Azureadb2cImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return } // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(f.HTTPClient) + keySet, err := c.FetchJWKs(deps.HTTPClient) if err != nil { return } var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( - f.HTTPClient, - f.Clock, + deps.HTTPClient, + deps.Clock, param.Code, keySet, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, @@ -103,7 +97,7 @@ func (f *Azureadb2cImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOp authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = sub - stdAttrs, err := f.Extract(claims) + stdAttrs, err := f.Extract(deps, claims) if err != nil { return } @@ -112,7 +106,7 @@ func (f *Azureadb2cImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOp return } -func (f *Azureadb2cImpl) Extract(claims map[string]interface{}) (stdattrs.T, error) { +func (f *Azureadb2cImpl) Extract(deps oauthrelyingparty.Dependencies, claims map[string]interface{}) (stdattrs.T, error) { // Here is the list of possible builtin claims of user flows // https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#user-flows // city: free text @@ -164,7 +158,7 @@ func (f *Azureadb2cImpl) Extract(claims map[string]interface{}) (stdattrs.T, err } out[stdattrs.Email] = email - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() return stdattrs.Extract(out, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/authn/sso/azureadb2c_test.go index 5775c139ec..6a803a3266 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/authn/sso/azureadb2c_test.go @@ -13,11 +13,11 @@ import ( func TestAzureadb2cImpl(t *testing.T) { Convey("Azureadb2cImpl", t, func() { - client := OAuthHTTPClient{&http.Client{}} - gock.InterceptClient(client.Client) + client := &http.Client{} + gock.InterceptClient(client) defer gock.Off() - g := &Azureadb2cImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": azureadb2c.Type, @@ -27,6 +27,8 @@ func TestAzureadb2cImpl(t *testing.T) { HTTPClient: client, } + g := &Azureadb2cImpl{} + gock.New("https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/v2.0/.well-known/openid-configuration"). Reply(200). BodyString(` @@ -36,7 +38,7 @@ func TestAzureadb2cImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index a9391d44b9..a04a894346 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -8,20 +8,14 @@ import ( "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" ) -type Azureadv2Impl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type Azureadv2Impl struct{} -func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) { +func (f *Azureadv2Impl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { // OPTIMIZE(sso): Cache OpenID configuration - tenant := azureadv2.ProviderConfig(f.ProviderConfig).Tenant() + tenant := azureadv2.ProviderConfig(deps.ProviderConfig).Tenant() var endpoint string // Azure special tenant @@ -63,18 +57,18 @@ func (f *Azureadv2Impl) getOpenIDConfiguration() (*OIDCDiscoveryDocument, error) endpoint = fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0/.well-known/openid-configuration", tenant) } - return FetchOIDCDiscoveryDocument(f.HTTPClient, endpoint) + return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } -func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration() +func (f *Azureadv2Impl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return "", err } return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -83,25 +77,25 @@ func (f *Azureadv2Impl) GetAuthorizationURL(param oauthrelyingparty.GetAuthoriza }), nil } -func (f *Azureadv2Impl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration() +func (f *Azureadv2Impl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := f.getOpenIDConfiguration(deps) if err != nil { return } // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(f.HTTPClient) + keySet, err := c.FetchJWKs(deps.HTTPClient) if err != nil { return } var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := c.ExchangeCode( - f.HTTPClient, - f.Clock, + deps.HTTPClient, + deps.Clock, param.Code, keySet, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, @@ -126,7 +120,7 @@ func (f *Azureadv2Impl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpt authInfo.ProviderRawProfile = claims authInfo.ProviderUserID = oid - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/authn/sso/azureadv2_test.go index f6b5f3f610..b5394b0831 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/authn/sso/azureadv2_test.go @@ -13,11 +13,11 @@ import ( func TestAzureadv2Impl(t *testing.T) { Convey("Azureadv2Impl", t, func() { - client := OAuthHTTPClient{&http.Client{}} - gock.InterceptClient(client.Client) + client := &http.Client{} + gock.InterceptClient(client) defer gock.Off() - g := &Azureadv2Impl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": azureadv2.Type, @@ -26,6 +26,8 @@ func TestAzureadv2Impl(t *testing.T) { HTTPClient: client, } + g := &Azureadv2Impl{} + gock.New("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"). Reply(200). BodyString(` @@ -99,7 +101,7 @@ func TestAzureadv2Impl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go index ddf6343bb8..081988eb8a 100644 --- a/pkg/lib/authn/sso/facebook.go +++ b/pkg/lib/authn/sso/facebook.go @@ -16,17 +16,13 @@ const ( facebookUserInfoURL string = "https://graph.facebook.com/v11.0/me?fields=id,email,first_name,last_name,middle_name,name,name_format,picture,short_name" ) -type FacebookImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type FacebookImpl struct{} -func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (f *FacebookImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset State: param.State, @@ -38,16 +34,16 @@ func (f *FacebookImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { +func (f *FacebookImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { authInfo = oauthrelyingparty.UserProfile{} accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( - f.HTTPClient.Client, + deps.HTTPClient, param.Code, facebookTokenURL, param.RedirectURI, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, ) if err != nil { return @@ -58,7 +54,7 @@ func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti return } q := userProfileURL.Query() - appSecretProof := crypto.HMACSHA256String([]byte(f.ClientSecret), []byte(accessTokenResp.AccessToken())) + appSecretProof := crypto.HMACSHA256String([]byte(deps.ClientSecret), []byte(accessTokenResp.AccessToken())) q.Set("appsecret_proof", appSecretProof) userProfileURL.RawQuery = q.Encode() @@ -81,7 +77,7 @@ func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti // "short_name": "John" // } - userProfile, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, userProfileURL.String()) + userProfile, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, userProfileURL.String()) if err != nil { return } @@ -103,7 +99,7 @@ func (f *FacebookImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti } authInfo.ProviderUserID = id - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(map[string]interface{}{ stdattrs.Email: email, stdattrs.GivenName: firstName, diff --git a/pkg/lib/authn/sso/facebook_test.go b/pkg/lib/authn/sso/facebook_test.go index 5798095784..4b9892f2b5 100644 --- a/pkg/lib/authn/sso/facebook_test.go +++ b/pkg/lib/authn/sso/facebook_test.go @@ -11,15 +11,15 @@ import ( func TestFacebookImpl(t *testing.T) { Convey("FacebookImpl", t, func() { - g := &FacebookImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": facebook.Type, }, - HTTPClient: OAuthHTTPClient{}, } + g := &FacebookImpl{} - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go index bf3e8d4431..579f56a257 100644 --- a/pkg/lib/authn/sso/github.go +++ b/pkg/lib/authn/sso/github.go @@ -21,18 +21,14 @@ const ( githubUserInfoURL string = "https://api.github.com/user" ) -type GithubImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type GithubImpl struct{} -func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (g *GithubImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: g.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: g.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), // ResponseType is unset. // ResponseMode is unset. State: param.State, @@ -41,13 +37,13 @@ func (g *GithubImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - accessTokenResp, err := g.exchangeCode(param) +func (g *GithubImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + accessTokenResp, err := g.exchangeCode(deps, param) if err != nil { return } - userProfile, err := g.fetchUserInfo(accessTokenResp) + userProfile, err := g.fetchUserInfo(deps, accessTokenResp) if err != nil { return } @@ -62,7 +58,7 @@ func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption id := string(idJSONNumber) authInfo.ProviderUserID = id - emailRequired := g.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(map[string]interface{}{ stdattrs.Email: email, stdattrs.Name: login, @@ -74,7 +70,7 @@ func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption }) if err != nil { err = apierrors.AddDetails(err, errorutil.Details{ - "ProviderType": apierrors.APIErrorDetail.Value(g.ProviderConfig.Type), + "ProviderType": apierrors.APIErrorDetail.Value(deps.ProviderConfig.Type()), }) return } @@ -83,10 +79,10 @@ func (g *GithubImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption return } -func (g *GithubImpl) exchangeCode(param oauthrelyingparty.GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { +func (g *GithubImpl) exchangeCode(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { q := make(url.Values) - q.Set("client_id", g.ProviderConfig.ClientID()) - q.Set("client_secret", g.ClientSecret) + q.Set("client_id", deps.ProviderConfig.ClientID()) + q.Set("client_secret", deps.ClientSecret) q.Set("code", param.Code) q.Set("redirect_uri", param.RedirectURI) @@ -96,7 +92,7 @@ func (g *GithubImpl) exchangeCode(param oauthrelyingparty.GetUserProfileOptions) req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - resp, err := g.HTTPClient.Do(req) + resp, err := deps.HTTPClient.Do(req) if err != nil { return } @@ -119,7 +115,7 @@ func (g *GithubImpl) exchangeCode(param oauthrelyingparty.GetUserProfileOptions) return } -func (g *GithubImpl) fetchUserInfo(accessTokenResp oauthrelyingpartyutil.AccessTokenResp) (userProfile map[string]interface{}, err error) { +func (g *GithubImpl) fetchUserInfo(deps oauthrelyingparty.Dependencies, accessTokenResp oauthrelyingpartyutil.AccessTokenResp) (userProfile map[string]interface{}, err error) { tokenType := accessTokenResp.TokenType() accessTokenValue := accessTokenResp.AccessToken() authorizationHeader := fmt.Sprintf("%s %s", tokenType, accessTokenValue) @@ -130,7 +126,7 @@ func (g *GithubImpl) fetchUserInfo(accessTokenResp oauthrelyingpartyutil.AccessT } req.Header.Add("Authorization", authorizationHeader) - resp, err := g.HTTPClient.Do(req) + resp, err := deps.HTTPClient.Do(req) if resp != nil { defer resp.Body.Close() } diff --git a/pkg/lib/authn/sso/github_test.go b/pkg/lib/authn/sso/github_test.go index 601fcc34ad..2ae2cb23d2 100644 --- a/pkg/lib/authn/sso/github_test.go +++ b/pkg/lib/authn/sso/github_test.go @@ -11,15 +11,15 @@ import ( func TestGithubImpl(t *testing.T) { Convey("GithubImpl", t, func() { - g := &GithubImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": github.Type, }, - HTTPClient: OAuthHTTPClient{}, } + g := &GithubImpl{} - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index 69aba3ac5c..ad558bfca1 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -6,29 +6,23 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" ) const ( googleOIDCDiscoveryDocumentURL string = "https://accounts.google.com/.well-known/openid-configuration" ) -type GoogleImpl struct { - Clock clock.Clock - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type GoogleImpl struct{} -func (f *GoogleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) +func (f *GoogleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + d, err := FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return "", err } return d.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, @@ -37,25 +31,25 @@ func (f *GoogleImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }), nil } -func (f *GoogleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - d, err := FetchOIDCDiscoveryDocument(f.HTTPClient, googleOIDCDiscoveryDocumentURL) +func (f *GoogleImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + d, err := FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return } // OPTIMIZE(sso): Cache JWKs - keySet, err := d.FetchJWKs(f.HTTPClient) + keySet, err := d.FetchJWKs(deps.HTTPClient) if err != nil { return } var tokenResp oauthrelyingpartyutil.AccessTokenResp jwtToken, err := d.ExchangeCode( - f.HTTPClient, - f.Clock, + deps.HTTPClient, + deps.Clock, param.Code, keySet, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, param.RedirectURI, param.Nonce, &tokenResp, @@ -93,7 +87,7 @@ func (f *GoogleImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOption // Google supports // given_name, family_name, email, picture, profile, locale // https://developers.google.com/identity/protocols/oauth2/openid-connect#obtainuserinfo - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/authn/sso/google_test.go index b9403494ff..a511d34c6e 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/authn/sso/google_test.go @@ -13,11 +13,11 @@ import ( func TestGoogleImpl(t *testing.T) { Convey("GoogleImpl", t, func() { - client := OAuthHTTPClient{&http.Client{}} - gock.InterceptClient(client.Client) + client := &http.Client{} + gock.InterceptClient(client) defer gock.Off() - g := &GoogleImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": google.Type, @@ -25,6 +25,8 @@ func TestGoogleImpl(t *testing.T) { HTTPClient: client, } + g := &GoogleImpl{} + gock.New(googleOIDCDiscoveryDocumentURL). Reply(200). BodyString(` @@ -89,7 +91,7 @@ func TestGoogleImpl(t *testing.T) { `) defer func() { gock.Flush() }() - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", ResponseMode: oauthrelyingparty.ResponseModeFormPost, Nonce: "nonce", diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go index f7c908d6e2..3f4fde9a58 100644 --- a/pkg/lib/authn/sso/linkedin.go +++ b/pkg/lib/authn/sso/linkedin.go @@ -14,17 +14,13 @@ const ( linkedinContactURL string = "https://api.linkedin.com/v2/clientAwareMemberHandles?q=members&projection=(elements*(primary,type,handle~))" ) -type LinkedInImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type LinkedInImpl struct{} -func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (f *LinkedInImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: f.ProviderConfig.ClientID(), + ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: f.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, @@ -36,25 +32,25 @@ func (f *LinkedInImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizat }.Query()), nil } -func (f *LinkedInImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { +func (f *LinkedInImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( - f.HTTPClient.Client, + deps.HTTPClient, param.Code, linkedinTokenURL, param.RedirectURI, - f.ProviderConfig.ClientID(), - f.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, ) if err != nil { return } - meResponse, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, linkedinMeURL) + meResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinMeURL) if err != nil { return } - contactResponse, err := oauthrelyingpartyutil.FetchUserProfile(f.HTTPClient.Client, accessTokenResp, linkedinContactURL) + contactResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinContactURL) if err != nil { return } @@ -267,7 +263,7 @@ func (f *LinkedInImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOpti id, attrs := decodeLinkedIn(combinedResponse) authInfo.ProviderUserID = id - emailRequired := f.ProviderConfig.EmailClaimConfig().Required() + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() attrs, err = stdattrs.Extract(attrs, stdattrs.ExtractOptions{ EmailRequired: emailRequired, }) diff --git a/pkg/lib/authn/sso/linkedin_test.go b/pkg/lib/authn/sso/linkedin_test.go index a3f014d49c..821f1fc983 100644 --- a/pkg/lib/authn/sso/linkedin_test.go +++ b/pkg/lib/authn/sso/linkedin_test.go @@ -11,15 +11,15 @@ import ( func TestLinkedInImpl(t *testing.T) { Convey("LinkedInImpl", t, func() { - g := &LinkedInImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": linkedin.Type, }, - HTTPClient: OAuthHTTPClient{}, } + g := &LinkedInImpl{} - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", Nonce: "nonce", State: "state", diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index c6f1860e2c..ec81c9c571 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -19,8 +19,8 @@ import ( // OAuthProvider is OAuth 2.0 based provider. type OAuthProvider interface { - GetAuthorizationURL(options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) - GetUserProfile(options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) + GetAuthorizationURL(deps oauthrelyingparty.Dependencies, options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) + GetUserProfile(deps oauthrelyingparty.Dependencies, options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) } type StandardAttributesNormalizer interface { @@ -43,7 +43,7 @@ func (p *OAuthProviderFactory) GetProviderConfig(alias string) (oauthrelyingpart return providerConfig, nil } -func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider, err error) { +func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider, deps *oauthrelyingparty.Dependencies, err error) { providerConfig, err := p.GetProviderConfig(alias) if err != nil { return @@ -55,66 +55,41 @@ func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider return } + deps = &oauthrelyingparty.Dependencies{ + Clock: p.Clock, + ProviderConfig: providerConfig, + ClientSecret: credentials.ClientSecret, + HTTPClient: p.HTTPClient.Client, + } + switch providerConfig.Type() { case google.Type: - return &GoogleImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &GoogleImpl{} + return case facebook.Type: - return &FacebookImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &FacebookImpl{} + return case github.Type: - return &GithubImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &GithubImpl{} + return case linkedin.Type: - return &LinkedInImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &LinkedInImpl{} + return case azureadv2.Type: - return &Azureadv2Impl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &Azureadv2Impl{} + return case azureadb2c.Type: - return &Azureadb2cImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &Azureadb2cImpl{} + return case adfs.Type: - return &ADFSImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &ADFSImpl{} + return case apple.Type: - return &AppleImpl{ - Clock: p.Clock, - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &AppleImpl{} + return case wechat.Type: - return &WechatImpl{ - ProviderConfig: providerConfig, - ClientSecret: credentials.ClientSecret, - HTTPClient: p.HTTPClient, - }, nil + provider = &WechatImpl{} + return default: // TODO(oauth): switch to registry-based resolution. err = api.ErrOAuthProviderNotFound @@ -123,21 +98,21 @@ func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider } func (p *OAuthProviderFactory) GetAuthorizationURL(alias string, options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) { - provider, err := p.getProvider(alias) + provider, deps, err := p.getProvider(alias) if err != nil { return } - return provider.GetAuthorizationURL(options) + return provider.GetAuthorizationURL(*deps, options) } func (p *OAuthProviderFactory) GetUserProfile(alias string, options oauthrelyingparty.GetUserProfileOptions) (userProfile oauthrelyingparty.UserProfile, err error) { - provider, err := p.getProvider(alias) + provider, deps, err := p.getProvider(alias) if err != nil { return } - userProfile, err = provider.GetUserProfile(options) + userProfile, err = provider.GetUserProfile(*deps, options) if err != nil { return } diff --git a/pkg/lib/authn/sso/oidc.go b/pkg/lib/authn/sso/oidc.go index 28719e2242..4a1fdb070c 100644 --- a/pkg/lib/authn/sso/oidc.go +++ b/pkg/lib/authn/sso/oidc.go @@ -13,13 +13,12 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwsutil" ) type jwtClock struct { - Clock clock.Clock + Clock oauthrelyingparty.Clock } func (c jwtClock) Now() time.Time { @@ -33,7 +32,7 @@ type OIDCDiscoveryDocument struct { JWKSUri string `json:"jwks_uri"` } -func FetchOIDCDiscoveryDocument(client OAuthHTTPClient, endpoint string) (*OIDCDiscoveryDocument, error) { +func FetchOIDCDiscoveryDocument(client *http.Client, endpoint string) (*OIDCDiscoveryDocument, error) { resp, err := client.Get(endpoint) if resp != nil { defer resp.Body.Close() @@ -62,7 +61,7 @@ func (d *OIDCDiscoveryDocument) MakeOAuthURL(params oauthrelyingpartyutil.Author return oauthrelyingpartyutil.MakeAuthorizationURL(d.AuthorizationEndpoint, params.Query()) } -func (d *OIDCDiscoveryDocument) FetchJWKs(client OAuthHTTPClient) (jwk.Set, error) { +func (d *OIDCDiscoveryDocument) FetchJWKs(client *http.Client) (jwk.Set, error) { resp, err := client.Get(d.JWKSUri) if resp != nil { defer resp.Body.Close() @@ -77,8 +76,8 @@ func (d *OIDCDiscoveryDocument) FetchJWKs(client OAuthHTTPClient) (jwk.Set, erro } func (d *OIDCDiscoveryDocument) ExchangeCode( - client OAuthHTTPClient, - clock clock.Clock, + client *http.Client, + clock oauthrelyingparty.Clock, code string, jwks jwk.Set, clientID string, diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 358cb3867f..309a2dbd34 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -11,18 +11,14 @@ const ( wechatAuthorizationURL string = "https://open.weixin.qq.com/connect/oauth2/authorize" ) -type WechatImpl struct { - ProviderConfig oauthrelyingparty.ProviderConfig - ClientSecret string - HTTPClient OAuthHTTPClient -} +type WechatImpl struct{} -func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (w *WechatImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. - WechatAppID: w.ProviderConfig.ClientID(), + WechatAppID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: w.ProviderConfig.Scope(), + Scope: deps.ProviderConfig.Scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, @@ -33,23 +29,23 @@ func (w *WechatImpl) GetAuthorizationURL(param oauthrelyingparty.GetAuthorizatio }.Query()), nil } -func (w *WechatImpl) GetUserProfile(param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { +func (w *WechatImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { accessTokenResp, err := wechatFetchAccessTokenResp( - w.HTTPClient, + deps.HTTPClient, param.Code, - w.ProviderConfig.ClientID(), - w.ClientSecret, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, ) if err != nil { return } - rawProfile, err := wechatFetchUserProfile(w.HTTPClient, accessTokenResp) + rawProfile, err := wechatFetchUserProfile(deps.HTTPClient, accessTokenResp) if err != nil { return } - is_sandbox_account := wechat.ProviderConfig(w.ProviderConfig).IsSandboxAccount() + is_sandbox_account := wechat.ProviderConfig(deps.ProviderConfig).IsSandboxAccount() var userID string if is_sandbox_account { if accessTokenResp.UnionID() != "" { diff --git a/pkg/lib/authn/sso/wechat_authinfo.go b/pkg/lib/authn/sso/wechat_authinfo.go index 4dd94c5e75..e691ae0715 100644 --- a/pkg/lib/authn/sso/wechat_authinfo.go +++ b/pkg/lib/authn/sso/wechat_authinfo.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/http" "net/url" ) @@ -60,7 +61,7 @@ func (r wechatUserInfoResp) OpenID() string { } func wechatFetchAccessTokenResp( - client OAuthHTTPClient, + client *http.Client, code string, appid string, secret string, @@ -110,7 +111,7 @@ func wechatFetchAccessTokenResp( } func wechatFetchUserProfile( - client OAuthHTTPClient, + client *http.Client, accessTokenResp wechatAccessTokenResp, ) (userProfile wechatUserInfoResp, err error) { v := url.Values{} diff --git a/pkg/lib/authn/sso/wechat_test.go b/pkg/lib/authn/sso/wechat_test.go index daf4b9ec90..8e91ff7a23 100644 --- a/pkg/lib/authn/sso/wechat_test.go +++ b/pkg/lib/authn/sso/wechat_test.go @@ -11,16 +11,16 @@ import ( func TestWechatImpl(t *testing.T) { Convey("WechatImpl", t, func() { - - g := &WechatImpl{ + deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", "type": wechat.Type, }, - HTTPClient: OAuthHTTPClient{}, } - u, err := g.GetAuthorizationURL(oauthrelyingparty.GetAuthorizationURLOptions{ + g := &WechatImpl{} + + u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ Nonce: "nonce", State: "state", Prompt: []string{"login"}, From afe5eb29f4203ff1a961524d123893b061e63542 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 16:57:19 +0800 Subject: [PATCH 19/30] Move wechat_authinfo to wechat --- pkg/lib/authn/sso/wechat.go | 155 +++++++++++++++++++++++++- pkg/lib/authn/sso/wechat_authinfo.go | 159 --------------------------- 2 files changed, 154 insertions(+), 160 deletions(-) delete mode 100644 pkg/lib/authn/sso/wechat_authinfo.go diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go index 309a2dbd34..2cb57264cd 100644 --- a/pkg/lib/authn/sso/wechat.go +++ b/pkg/lib/authn/sso/wechat.go @@ -1,6 +1,13 @@ package sso import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" @@ -8,7 +15,10 @@ import ( ) const ( - wechatAuthorizationURL string = "https://open.weixin.qq.com/connect/oauth2/authorize" + wechatAuthorizationURL = "https://open.weixin.qq.com/connect/oauth2/authorize" + // nolint: gosec + wechatAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token" + wechatUserInfoURL = "https://api.weixin.qq.com/sns/userinfo" ) type WechatImpl struct{} @@ -104,3 +114,146 @@ func (w *WechatImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param o var ( _ OAuthProvider = &WechatImpl{} ) + +type wechatOAuthErrorResp struct { + ErrorCode int `json:"errcode"` + ErrorMsg string `json:"errmsg"` +} + +func (r *wechatOAuthErrorResp) AsError() error { + return fmt.Errorf("wechat: %d: %s", r.ErrorCode, r.ErrorMsg) +} + +type wechatAccessTokenResp map[string]interface{} + +func (r wechatAccessTokenResp) AccessToken() string { + accessToken, ok := r["access_token"].(string) + if ok { + return accessToken + } + return "" +} + +func (r wechatAccessTokenResp) OpenID() string { + openid, ok := r["openid"].(string) + if ok { + return openid + } + return "" +} + +func (r wechatAccessTokenResp) UnionID() string { + unionid, ok := r["unionid"].(string) + if ok { + return unionid + } + return "" +} + +type wechatUserInfoResp map[string]interface{} + +func (r wechatUserInfoResp) OpenID() string { + openid, ok := r["openid"].(string) + if ok { + return openid + } + return "" +} + +func wechatFetchAccessTokenResp( + client *http.Client, + code string, + appid string, + secret string, +) (r wechatAccessTokenResp, err error) { + v := url.Values{} + v.Set("grant_type", "authorization_code") + v.Add("code", code) + v.Add("appid", appid) + v.Add("secret", secret) + + resp, err := client.PostForm(wechatAccessTokenURL, v) + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + return + } + + // wechat always return 200 + // to know if there is error, we need to parse the response body + if resp.StatusCode != 200 { + err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) + return + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + err = json.NewDecoder(bytes.NewReader(body)).Decode(&r) + if err != nil { + return + } + if r.AccessToken() == "" { + // failed to obtain access token, parse the error response + var errResp wechatOAuthErrorResp + err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) + if err != nil { + return + } + err = errResp.AsError() + return + } + return +} + +func wechatFetchUserProfile( + client *http.Client, + accessTokenResp wechatAccessTokenResp, +) (userProfile wechatUserInfoResp, err error) { + v := url.Values{} + v.Set("openid", accessTokenResp.OpenID()) + v.Set("access_token", accessTokenResp.AccessToken()) + v.Set("lang", "en") + + resp, err := client.PostForm(wechatUserInfoURL, v) + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + return + } + + // wechat always return 200 + // to know if there is error, we need to parse the response body + if resp.StatusCode != 200 { + err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) + return + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + err = json.NewDecoder(bytes.NewReader(body)).Decode(&userProfile) + if err != nil { + return + } + if userProfile.OpenID() == "" { + // failed to obtain id from user info, parse the error response + var errResp wechatOAuthErrorResp + err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) + if err != nil { + return + } + err = errResp.AsError() + return + } + + return +} diff --git a/pkg/lib/authn/sso/wechat_authinfo.go b/pkg/lib/authn/sso/wechat_authinfo.go deleted file mode 100644 index e691ae0715..0000000000 --- a/pkg/lib/authn/sso/wechat_authinfo.go +++ /dev/null @@ -1,159 +0,0 @@ -package sso - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" -) - -const ( - // nolint: gosec - wechatAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token" - wechatUserInfoURL = "https://api.weixin.qq.com/sns/userinfo" -) - -type wechatOAuthErrorResp struct { - ErrorCode int `json:"errcode"` - ErrorMsg string `json:"errmsg"` -} - -func (r *wechatOAuthErrorResp) AsError() error { - return fmt.Errorf("wechat: %d: %s", r.ErrorCode, r.ErrorMsg) -} - -type wechatAccessTokenResp map[string]interface{} - -func (r wechatAccessTokenResp) AccessToken() string { - accessToken, ok := r["access_token"].(string) - if ok { - return accessToken - } - return "" -} - -func (r wechatAccessTokenResp) OpenID() string { - openid, ok := r["openid"].(string) - if ok { - return openid - } - return "" -} - -func (r wechatAccessTokenResp) UnionID() string { - unionid, ok := r["unionid"].(string) - if ok { - return unionid - } - return "" -} - -type wechatUserInfoResp map[string]interface{} - -func (r wechatUserInfoResp) OpenID() string { - openid, ok := r["openid"].(string) - if ok { - return openid - } - return "" -} - -func wechatFetchAccessTokenResp( - client *http.Client, - code string, - appid string, - secret string, -) (r wechatAccessTokenResp, err error) { - v := url.Values{} - v.Set("grant_type", "authorization_code") - v.Add("code", code) - v.Add("appid", appid) - v.Add("secret", secret) - - resp, err := client.PostForm(wechatAccessTokenURL, v) - if resp != nil { - defer resp.Body.Close() - } - - if err != nil { - return - } - - // wechat always return 200 - // to know if there is error, we need to parse the response body - if resp.StatusCode != 200 { - err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) - return - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - err = json.NewDecoder(bytes.NewReader(body)).Decode(&r) - if err != nil { - return - } - if r.AccessToken() == "" { - // failed to obtain access token, parse the error response - var errResp wechatOAuthErrorResp - err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) - if err != nil { - return - } - err = errResp.AsError() - return - } - return -} - -func wechatFetchUserProfile( - client *http.Client, - accessTokenResp wechatAccessTokenResp, -) (userProfile wechatUserInfoResp, err error) { - v := url.Values{} - v.Set("openid", accessTokenResp.OpenID()) - v.Set("access_token", accessTokenResp.AccessToken()) - v.Set("lang", "en") - - resp, err := client.PostForm(wechatUserInfoURL, v) - if resp != nil { - defer resp.Body.Close() - } - - if err != nil { - return - } - - // wechat always return 200 - // to know if there is error, we need to parse the response body - if resp.StatusCode != 200 { - err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) - return - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - err = json.NewDecoder(bytes.NewReader(body)).Decode(&userProfile) - if err != nil { - return - } - if userProfile.OpenID() == "" { - // failed to obtain id from user info, parse the error response - var errResp wechatOAuthErrorResp - err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) - if err != nil { - return - } - err = errResp.AsError() - return - } - - return -} From 76c82330d0a39c4208c3758a37a1af34f832fb11 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 17:55:23 +0800 Subject: [PATCH 20/30] Move oidc.go to oauthrelyingpartyutil --- pkg/lib/authn/sso/adfs.go | 4 ++-- pkg/lib/authn/sso/apple.go | 2 +- pkg/lib/authn/sso/azureadb2c.go | 4 ++-- pkg/lib/authn/sso/azureadv2.go | 4 ++-- pkg/lib/authn/sso/google.go | 4 ++-- .../oauthrelyingparty/oauthrelyingpartyutil/error.go | 2 ++ .../oauthrelyingpartyutil}/oidc.go | 11 +++++------ 7 files changed, 16 insertions(+), 15 deletions(-) rename pkg/lib/{authn/sso => oauthrelyingparty/oauthrelyingpartyutil}/oidc.go (89%) diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go index ece7a26b7d..0cbc640912 100644 --- a/pkg/lib/authn/sso/adfs.go +++ b/pkg/lib/authn/sso/adfs.go @@ -12,9 +12,9 @@ import ( type ADFSImpl struct{} -func (f *ADFSImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { +func (f *ADFSImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { endpoint := adfs.ProviderConfig(deps.ProviderConfig).DiscoveryDocumentEndpoint() - return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } func (f *ADFSImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go index 6b74559dc8..6e6ea6ff66 100644 --- a/pkg/lib/authn/sso/apple.go +++ b/pkg/lib/authn/sso/apple.go @@ -17,7 +17,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/jwtutil" ) -var appleOIDCConfig = OIDCDiscoveryDocument{ +var appleOIDCConfig = oauthrelyingpartyutil.OIDCDiscoveryDocument{ JWKSUri: "https://appleid.apple.com/auth/keys", TokenEndpoint: "https://appleid.apple.com/auth/token", AuthorizationEndpoint: "https://appleid.apple.com/auth/authorize", diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go index 3fe7ee62fa..4f299cf9af 100644 --- a/pkg/lib/authn/sso/azureadb2c.go +++ b/pkg/lib/authn/sso/azureadb2c.go @@ -12,7 +12,7 @@ import ( type Azureadb2cImpl struct{} -func (f *Azureadb2cImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { +func (f *Azureadb2cImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { azureadb2cConfig := azureadb2c.ProviderConfig(deps.ProviderConfig) tenant := azureadb2cConfig.Tenant() policy := azureadb2cConfig.Policy() @@ -24,7 +24,7 @@ func (f *Azureadb2cImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependenc policy, ) - return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } func (f *Azureadb2cImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go index a04a894346..d4f4cda4e8 100644 --- a/pkg/lib/authn/sso/azureadv2.go +++ b/pkg/lib/authn/sso/azureadv2.go @@ -12,7 +12,7 @@ import ( type Azureadv2Impl struct{} -func (f *Azureadv2Impl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*OIDCDiscoveryDocument, error) { +func (f *Azureadv2Impl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { // OPTIMIZE(sso): Cache OpenID configuration tenant := azureadv2.ProviderConfig(deps.ProviderConfig).Tenant() @@ -57,7 +57,7 @@ func (f *Azureadv2Impl) getOpenIDConfiguration(deps oauthrelyingparty.Dependenci endpoint = fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0/.well-known/openid-configuration", tenant) } - return FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) } func (f *Azureadv2Impl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go index ad558bfca1..cf902f8ccd 100644 --- a/pkg/lib/authn/sso/google.go +++ b/pkg/lib/authn/sso/google.go @@ -15,7 +15,7 @@ const ( type GoogleImpl struct{} func (f *GoogleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - d, err := FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) + d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return "", err } @@ -32,7 +32,7 @@ func (f *GoogleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, pa } func (f *GoogleImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - d, err := FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) + d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) if err != nil { return } diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go index 9e4f4ffacb..3d488ae6e3 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go @@ -5,6 +5,8 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" ) +var InvalidConfiguration = apierrors.InternalError.WithReason("InvalidConfiguration") +var OAuthProtocolError = apierrors.BadRequest.WithReason("OAuthProtocolError") var OAuthError = apierrors.BadRequest.WithReason("OAuthError") func NewOAuthError(errorString string, errorDescription string, errorURI string) error { diff --git a/pkg/lib/authn/sso/oidc.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go similarity index 89% rename from pkg/lib/authn/sso/oidc.go rename to pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go index 4a1fdb070c..2b36e5b29c 100644 --- a/pkg/lib/authn/sso/oidc.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go @@ -1,4 +1,4 @@ -package sso +package oauthrelyingpartyutil import ( "crypto/subtle" @@ -12,7 +12,6 @@ import ( "github.com/lestrrat-go/jwx/v2/jwt" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwsutil" ) @@ -57,8 +56,8 @@ func FetchOIDCDiscoveryDocument(client *http.Client, endpoint string) (*OIDCDisc return &document, nil } -func (d *OIDCDiscoveryDocument) MakeOAuthURL(params oauthrelyingpartyutil.AuthorizationURLParams) string { - return oauthrelyingpartyutil.MakeAuthorizationURL(d.AuthorizationEndpoint, params.Query()) +func (d *OIDCDiscoveryDocument) MakeOAuthURL(params AuthorizationURLParams) string { + return MakeAuthorizationURL(d.AuthorizationEndpoint, params.Query()) } func (d *OIDCDiscoveryDocument) FetchJWKs(client *http.Client) (jwk.Set, error) { @@ -84,7 +83,7 @@ func (d *OIDCDiscoveryDocument) ExchangeCode( clientSecret string, redirectURI string, nonce string, - tokenResp *oauthrelyingpartyutil.AccessTokenResp, + tokenResp *AccessTokenResp, ) (jwt.Token, error) { body := url.Values{} body.Set("grant_type", "authorization_code") @@ -110,7 +109,7 @@ func (d *OIDCDiscoveryDocument) ExchangeCode( if err != nil { return nil, err } - err = oauthrelyingpartyutil.ErrorResponseAsError(errorResp) + err = ErrorResponseAsError(errorResp) return nil, err } From 6b2a401df43c43c9dbaa4294cdcdd287a4d6cbb7 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 18:47:25 +0800 Subject: [PATCH 21/30] Move implementations to oauthrelyingparty --- cmd/authgear/main.go | 9 + cmd/portal/main.go | 9 + pkg/api/oauthrelyingparty/provider.go | 2 + pkg/lib/authn/sso/adfs.go | 129 ------- pkg/lib/authn/sso/apple.go | 154 -------- pkg/lib/authn/sso/azureadb2c.go | 180 ---------- pkg/lib/authn/sso/azureadv2.go | 155 -------- pkg/lib/authn/sso/facebook.go | 123 ------- pkg/lib/authn/sso/github.go | 155 -------- pkg/lib/authn/sso/google.go | 123 ------- pkg/lib/authn/sso/linkedin.go | 335 ------------------ pkg/lib/authn/sso/oauth_provider.go | 52 +-- pkg/lib/authn/sso/wechat.go | 259 -------------- pkg/lib/config/feature_identity.go | 28 +- pkg/lib/oauthrelyingparty/adfs/provider.go | 115 ++++++ .../adfs/provider_test.go} | 11 +- pkg/lib/oauthrelyingparty/apple/provider.go | 141 ++++++++ .../apple/provider_test.go} | 7 +- .../oauthrelyingparty/azureadb2c/provider.go | 167 +++++++++ .../azureadb2c/provider_test.go} | 7 +- .../oauthrelyingparty/azureadv2/provider.go | 142 ++++++++ .../azureadv2/provider_test.go} | 7 +- .../oauthrelyingparty/facebook/provider.go | 111 ++++++ .../facebook/provider_test.go} | 11 +- pkg/lib/oauthrelyingparty/github/provider.go | 143 ++++++++ .../github/provider_test.go} | 11 +- pkg/lib/oauthrelyingparty/google/provider.go | 111 ++++++ .../google/provider_test.go} | 7 +- .../oauthrelyingparty/linkedin/provider.go | 323 +++++++++++++++++ .../linkedin/provider_test.go} | 11 +- pkg/lib/oauthrelyingparty/wechat/provider.go | 245 +++++++++++++ .../wechat/provider_test.go} | 11 +- 32 files changed, 1568 insertions(+), 1726 deletions(-) delete mode 100644 pkg/lib/authn/sso/adfs.go delete mode 100644 pkg/lib/authn/sso/apple.go delete mode 100644 pkg/lib/authn/sso/azureadb2c.go delete mode 100644 pkg/lib/authn/sso/azureadv2.go delete mode 100644 pkg/lib/authn/sso/facebook.go delete mode 100644 pkg/lib/authn/sso/github.go delete mode 100644 pkg/lib/authn/sso/google.go delete mode 100644 pkg/lib/authn/sso/linkedin.go delete mode 100644 pkg/lib/authn/sso/wechat.go rename pkg/lib/{authn/sso/adfs_test.go => oauthrelyingparty/adfs/provider_test.go} (85%) rename pkg/lib/{authn/sso/apple_test.go => oauthrelyingparty/apple/provider_test.go} (86%) rename pkg/lib/{authn/sso/azureadb2c_test.go => oauthrelyingparty/azureadb2c/provider_test.go} (89%) rename pkg/lib/{authn/sso/azureadv2_test.go => oauthrelyingparty/azureadv2/provider_test.go} (95%) rename pkg/lib/{authn/sso/facebook_test.go => oauthrelyingparty/facebook/provider_test.go} (76%) rename pkg/lib/{authn/sso/github_test.go => oauthrelyingparty/github/provider_test.go} (76%) rename pkg/lib/{authn/sso/google_test.go => oauthrelyingparty/google/provider_test.go} (94%) rename pkg/lib/{authn/sso/linkedin_test.go => oauthrelyingparty/linkedin/provider_test.go} (76%) rename pkg/lib/{authn/sso/wechat_test.go => oauthrelyingparty/wechat/provider_test.go} (75%) diff --git a/cmd/authgear/main.go b/cmd/authgear/main.go index 66beda7014..7baffbaf1f 100644 --- a/cmd/authgear/main.go +++ b/cmd/authgear/main.go @@ -21,6 +21,15 @@ import ( _ "github.com/authgear/authgear-server/cmd/authgear/cmd/cmdstart" _ "github.com/authgear/authgear-server/pkg/latte" _ "github.com/authgear/authgear-server/pkg/lib/authenticationflow/declarative" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/debug" ) diff --git a/cmd/portal/main.go b/cmd/portal/main.go index 2b6b4a702f..e87256de90 100644 --- a/cmd/portal/main.go +++ b/cmd/portal/main.go @@ -16,6 +16,15 @@ import ( _ "github.com/authgear/authgear-server/cmd/portal/cmd/cmdpricing" _ "github.com/authgear/authgear-server/cmd/portal/cmd/cmdstart" _ "github.com/authgear/authgear-server/cmd/portal/cmd/cmdusage" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/debug" ) diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go index 92f68051a6..5fc74e2e71 100644 --- a/pkg/api/oauthrelyingparty/provider.go +++ b/pkg/api/oauthrelyingparty/provider.go @@ -176,4 +176,6 @@ type Provider interface { SetDefaults(cfg ProviderConfig) ProviderID(cfg ProviderConfig) ProviderID Scope(cfg ProviderConfig) []string + GetAuthorizationURL(deps Dependencies, options GetAuthorizationURLOptions) (url string, err error) + GetUserProfile(deps Dependencies, options GetUserProfileOptions) (UserProfile, error) } diff --git a/pkg/lib/authn/sso/adfs.go b/pkg/lib/authn/sso/adfs.go deleted file mode 100644 index 0cbc640912..0000000000 --- a/pkg/lib/authn/sso/adfs.go +++ /dev/null @@ -1,129 +0,0 @@ -package sso - -import ( - "context" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/validation" -) - -type ADFSImpl struct{} - -func (f *ADFSImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { - endpoint := adfs.ProviderConfig(deps.ProviderConfig).DiscoveryDocumentEndpoint() - return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) -} - -func (f *ADFSImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return "", err - } - return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - ResponseMode: param.ResponseMode, - State: param.State, - Prompt: f.getPrompt(param.Prompt), - Nonce: param.Nonce, - }), nil -} - -func (f *ADFSImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return - } - - // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(deps.HTTPClient) - if err != nil { - return - } - - var tokenResp oauthrelyingpartyutil.AccessTokenResp - jwtToken, err := c.ExchangeCode( - deps.HTTPClient, - deps.Clock, - param.Code, - keySet, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - param.RedirectURI, - param.Nonce, - &tokenResp, - ) - if err != nil { - return - } - - claims, err := jwtToken.AsMap(context.TODO()) - if err != nil { - return - } - - sub, ok := claims["sub"].(string) - if !ok { - err = OAuthProtocolError.New("sub not found in ID token") - return - } - - // The upn claim is documented here. - // https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/configuring-alternate-login-id - upn, ok := claims["upn"].(string) - if !ok { - err = OAuthProtocolError.New("upn not found in ID token") - return - } - - extracted, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{}) - if err != nil { - return - } - - // Transform upn into preferred_username - if _, ok := extracted[stdattrs.PreferredUsername]; !ok { - extracted[stdattrs.PreferredUsername] = upn - } - // Transform upn into email - if _, ok := extracted[stdattrs.Email]; !ok { - if emailErr := (validation.FormatEmail{}).CheckFormat(upn); emailErr == nil { - // upn looks like an email address. - extracted[stdattrs.Email] = upn - } - } - - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - extracted, err = stdattrs.Extract(extracted, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = extracted - - authInfo.ProviderRawProfile = claims - authInfo.ProviderUserID = sub - - return -} - -func (f *ADFSImpl) getPrompt(prompt []string) []string { - // ADFS only supports prompt=login - // https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/ad-fs-prompt-login - for _, p := range prompt { - if p == "login" { - return []string{"login"} - } - } - return []string{} -} - -var ( - _ OAuthProvider = &ADFSImpl{} -) diff --git a/pkg/lib/authn/sso/apple.go b/pkg/lib/authn/sso/apple.go deleted file mode 100644 index 6e6ea6ff66..0000000000 --- a/pkg/lib/authn/sso/apple.go +++ /dev/null @@ -1,154 +0,0 @@ -package sso - -import ( - "context" - "strings" - - "github.com/lestrrat-go/jwx/v2/jwa" - "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/lestrrat-go/jwx/v2/jwt" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/crypto" - "github.com/authgear/authgear-server/pkg/util/duration" - "github.com/authgear/authgear-server/pkg/util/jwtutil" -) - -var appleOIDCConfig = oauthrelyingpartyutil.OIDCDiscoveryDocument{ - JWKSUri: "https://appleid.apple.com/auth/keys", - TokenEndpoint: "https://appleid.apple.com/auth/token", - AuthorizationEndpoint: "https://appleid.apple.com/auth/authorize", -} - -type AppleImpl struct{} - -func (f *AppleImpl) createClientSecret(deps oauthrelyingparty.Dependencies) (clientSecret string, err error) { - teamID := apple.ProviderConfig(deps.ProviderConfig).TeamID() - keyID := apple.ProviderConfig(deps.ProviderConfig).KeyID() - - // https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens - key, err := crypto.ParseAppleP8PrivateKey([]byte(deps.ClientSecret)) - if err != nil { - return - } - - now := deps.Clock.NowUTC() - - payload := jwt.New() - _ = payload.Set(jwt.IssuerKey, teamID) - _ = payload.Set(jwt.IssuedAtKey, now.Unix()) - _ = payload.Set(jwt.ExpirationKey, now.Add(duration.Short).Unix()) - _ = payload.Set(jwt.AudienceKey, "https://appleid.apple.com") - _ = payload.Set(jwt.SubjectKey, deps.ProviderConfig.ClientID) - - jwkKey, err := jwk.FromRaw(key) - if err != nil { - return - } - _ = jwkKey.Set("kid", keyID) - - token, err := jwtutil.Sign(payload, jwa.ES256, jwkKey) - if err != nil { - return - } - - clientSecret = string(token) - return -} - -func (f *AppleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - ResponseMode: param.ResponseMode, - State: param.State, - // Prompt is unset. - // Apple doesn't support prompt parameter - // See "Send the Required Query Parameters" section for supporting parameters - // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms - Nonce: param.Nonce, - }), nil -} - -func (f *AppleImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - keySet, err := appleOIDCConfig.FetchJWKs(deps.HTTPClient) - if err != nil { - return - } - - clientSecret, err := f.createClientSecret(deps) - if err != nil { - return - } - - var tokenResp oauthrelyingpartyutil.AccessTokenResp - jwtToken, err := appleOIDCConfig.ExchangeCode( - deps.HTTPClient, - deps.Clock, - param.Code, - keySet, - deps.ProviderConfig.ClientID(), - clientSecret, - param.RedirectURI, - param.Nonce, - &tokenResp, - ) - if err != nil { - return - } - - claims, err := jwtToken.AsMap(context.TODO()) - if err != nil { - return - } - - // Verify the issuer - // https://developer.apple.com/documentation/signinwithapplerestapi/verifying_a_user - // The exact spec is - // Verify that the iss field contains https://appleid.apple.com - // Therefore, we use strings.Contains here. - iss, ok := claims["iss"].(string) - if !ok { - err = OAuthProtocolError.New("iss not found in ID token") - return - } - if !strings.Contains(iss, "https://appleid.apple.com") { - err = OAuthProtocolError.New("iss does not equal to `https://appleid.apple.com`") - return - } - - // Ensure sub exists - sub, ok := claims["sub"].(string) - if !ok { - err = OAuthProtocolError.New("sub not found in ID Token") - return - } - - // By observation, if the first time of authentication does NOT include the `name` scope, - // Even the Services ID is unauthorized on https://appleid.apple.com, - // and the `name` scope is included, - // The ID Token still does not include the `name` claim. - - authInfo.ProviderRawProfile = claims - authInfo.ProviderUserID = sub - - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = stdAttrs.WithNameCopiedToGivenName() - - return -} - -var ( - _ OAuthProvider = &AppleImpl{} -) diff --git a/pkg/lib/authn/sso/azureadb2c.go b/pkg/lib/authn/sso/azureadb2c.go deleted file mode 100644 index 4f299cf9af..0000000000 --- a/pkg/lib/authn/sso/azureadb2c.go +++ /dev/null @@ -1,180 +0,0 @@ -package sso - -import ( - "context" - "fmt" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" -) - -type Azureadb2cImpl struct{} - -func (f *Azureadb2cImpl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { - azureadb2cConfig := azureadb2c.ProviderConfig(deps.ProviderConfig) - tenant := azureadb2cConfig.Tenant() - policy := azureadb2cConfig.Policy() - - endpoint := fmt.Sprintf( - "https://%s.b2clogin.com/%s.onmicrosoft.com/%s/v2.0/.well-known/openid-configuration", - tenant, - tenant, - policy, - ) - - return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) -} - -func (f *Azureadb2cImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return "", err - } - return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - ResponseMode: param.ResponseMode, - State: param.State, - Prompt: f.getPrompt(param.Prompt), - Nonce: param.Nonce, - }), nil -} - -func (f *Azureadb2cImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return - } - // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(deps.HTTPClient) - if err != nil { - return - } - - var tokenResp oauthrelyingpartyutil.AccessTokenResp - jwtToken, err := c.ExchangeCode( - deps.HTTPClient, - deps.Clock, - param.Code, - keySet, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - param.RedirectURI, - param.Nonce, - &tokenResp, - ) - if err != nil { - return - } - - claims, err := jwtToken.AsMap(context.TODO()) - if err != nil { - return - } - - iss, ok := claims["iss"].(string) - if !ok { - err = OAuthProtocolError.New("iss not found in ID token") - return - } - if iss != c.Issuer { - err = OAuthProtocolError.New( - fmt.Sprintf("iss: %v != %v", iss, c.Issuer), - ) - return - } - - sub, ok := claims["sub"].(string) - if !ok || sub == "" { - err = OAuthProtocolError.New("sub not found in ID Token") - return - } - - authInfo.ProviderRawProfile = claims - authInfo.ProviderUserID = sub - - stdAttrs, err := f.Extract(deps, claims) - if err != nil { - return - } - authInfo.StandardAttributes = stdAttrs - - return -} - -func (f *Azureadb2cImpl) Extract(deps oauthrelyingparty.Dependencies, claims map[string]interface{}) (stdattrs.T, error) { - // Here is the list of possible builtin claims of user flows - // https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#user-flows - // city: free text - // country: free text - // jobTitle: free text - // legalAgeGroupClassification: a enum with undocumented variants - // postalCode: free text - // state: free text - // streetAddress: free text - // newUser: true means the user signed up newly - // oid: sub is identical to it by default. - // emails: if non-empty, the first value corresponds to standard claim - // name: correspond to standard claim - // given_name: correspond to standard claim - // family_name: correspond to standard claim - - // For custom policy we further recognize the following claims. - // https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-profile-attributes - // signInNames.emailAddress: string - - extractString := func(input map[string]interface{}, output stdattrs.T, key string) { - if value, ok := input[key].(string); ok && value != "" { - output[key] = value - } - } - - out := stdattrs.T{} - - extractString(claims, out, stdattrs.Name) - extractString(claims, out, stdattrs.GivenName) - extractString(claims, out, stdattrs.FamilyName) - - var email string - if email == "" { - if ifaceSlice, ok := claims["emails"].([]interface{}); ok { - for _, iface := range ifaceSlice { - if str, ok := iface.(string); ok && str != "" { - email = str - } - } - } - } - if email == "" { - if str, ok := claims["signInNames.emailAddress"].(string); ok { - if str != "" { - email = str - } - } - } - out[stdattrs.Email] = email - - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - return stdattrs.Extract(out, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) -} - -func (f *Azureadb2cImpl) getPrompt(prompt []string) []string { - // The only supported value is login. - // See https://docs.microsoft.com/en-us/azure/active-directory-b2c/openid-connect - for _, p := range prompt { - if p == "login" { - return []string{"login"} - } - } - return []string{} -} - -var ( - _ OAuthProvider = &Azureadb2cImpl{} -) diff --git a/pkg/lib/authn/sso/azureadv2.go b/pkg/lib/authn/sso/azureadv2.go deleted file mode 100644 index d4f4cda4e8..0000000000 --- a/pkg/lib/authn/sso/azureadv2.go +++ /dev/null @@ -1,155 +0,0 @@ -package sso - -import ( - "context" - "fmt" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" -) - -type Azureadv2Impl struct{} - -func (f *Azureadv2Impl) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { - // OPTIMIZE(sso): Cache OpenID configuration - - tenant := azureadv2.ProviderConfig(deps.ProviderConfig).Tenant() - - var endpoint string - // Azure special tenant - // - // If the azure tenant is `organizations` or `common`, - // the developer should make use of `before_user_create` and `before_identity_create` hook - // to disallow any undesire identity. - // The `raw_profile` of the identity is the ID Token claims. - // Refer to https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens - // to see what claims the token could contain. - // - // For `organizations`, the user can be any user of any organizational AD. - // Therefore the developer should have a whitelist of AD tenant IDs. - // In the incoming hook, check if `tid` matches one of the entry of the whitelist. - // - // For `common`, in addition to the users from `organizations`, any Microsoft personal account - // could be the user. - // In case of personal account, the `tid` is "9188040d-6c67-4c5b-b112-36a304b66dad". - // Therefore the developer should first check if `tid` indicates personal account. - // If yes, apply their logic to disallow the user creation. - // One very common example is to look at the claim `email`. - // Use a email address parser to parse the email address. - // Obtain the domain and check if the domain is whitelisted. - // For example, if the developer only wants user from hotmail.com to create user, - // ensure `tid` is "9188040d-6c67-4c5b-b112-36a304b66dad" and ensure `email` - // is of domain `@hotmail.com`. - - // As of 2019-09-23, two special values are observed. - // To discover these values, create a new client - // and try different options. - switch tenant { - // Special value for any organizational AD - case "organizations": - endpoint = "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration" - // Special value for any organizational AD and personal accounts (Xbox etc) - case "common": - endpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration" - default: - endpoint = fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0/.well-known/openid-configuration", tenant) - } - - return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) -} - -func (f *Azureadv2Impl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return "", err - } - return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - ResponseMode: param.ResponseMode, - State: param.State, - Prompt: f.getPrompt(param.Prompt), - Nonce: param.Nonce, - }), nil -} - -func (f *Azureadv2Impl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - c, err := f.getOpenIDConfiguration(deps) - if err != nil { - return - } - // OPTIMIZE(sso): Cache JWKs - keySet, err := c.FetchJWKs(deps.HTTPClient) - if err != nil { - return - } - - var tokenResp oauthrelyingpartyutil.AccessTokenResp - jwtToken, err := c.ExchangeCode( - deps.HTTPClient, - deps.Clock, - param.Code, - keySet, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - param.RedirectURI, - param.Nonce, - &tokenResp, - ) - if err != nil { - return - } - - claims, err := jwtToken.AsMap(context.TODO()) - if err != nil { - return - } - - oid, ok := claims["oid"].(string) - if !ok { - err = OAuthProtocolError.New("oid not found in ID Token") - return - } - // For "Microsoft Account", email usually exists. - // For "AD guest user", email usually exists because to invite an user, the inviter must provide email. - // For "AD user", email never exists even one is provided in "Authentication Methods". - - authInfo.ProviderRawProfile = claims - authInfo.ProviderUserID = oid - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = stdAttrs - - return -} - -func (f *Azureadv2Impl) getPrompt(prompt []string) []string { - // Azureadv2 only supports single value for prompt. - // The first supporting value in the list will be used. - // The usage of `none` is for checking existing authentication and/or consent - // which doesn't fit auth ui case. - // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow - for _, p := range prompt { - if p == "login" { - return []string{"login"} - } else if p == "consent" { - return []string{"consent"} - } else if p == "select_account" { - return []string{"select_account"} - } - } - return []string{} -} - -var ( - _ OAuthProvider = &Azureadv2Impl{} -) diff --git a/pkg/lib/authn/sso/facebook.go b/pkg/lib/authn/sso/facebook.go deleted file mode 100644 index 081988eb8a..0000000000 --- a/pkg/lib/authn/sso/facebook.go +++ /dev/null @@ -1,123 +0,0 @@ -package sso - -import ( - "net/url" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/crypto" -) - -const ( - facebookAuthorizationURL string = "https://www.facebook.com/v11.0/dialog/oauth" - // nolint: gosec - facebookTokenURL string = "https://graph.facebook.com/v11.0/oauth/access_token" - facebookUserInfoURL string = "https://graph.facebook.com/v11.0/me?fields=id,email,first_name,last_name,middle_name,name,name_format,picture,short_name" -) - -type FacebookImpl struct{} - -func (f *FacebookImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - // ResponseMode is unset - State: param.State, - // Prompt is unset. - // Facebook doesn't support prompt parameter - // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/ - - // Nonce is unset - }.Query()), nil -} - -func (f *FacebookImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - authInfo = oauthrelyingparty.UserProfile{} - - accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( - deps.HTTPClient, - param.Code, - facebookTokenURL, - param.RedirectURI, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - ) - if err != nil { - return - } - - userProfileURL, err := url.Parse(facebookUserInfoURL) - if err != nil { - return - } - q := userProfileURL.Query() - appSecretProof := crypto.HMACSHA256String([]byte(deps.ClientSecret), []byte(accessTokenResp.AccessToken())) - q.Set("appsecret_proof", appSecretProof) - userProfileURL.RawQuery = q.Encode() - - // Here is the refacted user profile of Louis' facebook account. - // { - // "id": "redacted", - // "email": "redacted", - // "first_name": "Jonathan", - // "last_name": "Doe", - // "name": "Johnathan Doe", - // "name_format": "{first} {last}", - // "picture": { - // "data": { - // "height": 50, - // "is_silhouette": true, - // "url": "http://example.com", - // "width": 50 - // } - // }, - // "short_name": "John" - // } - - userProfile, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, userProfileURL.String()) - if err != nil { - return - } - authInfo.ProviderRawProfile = userProfile - - id, _ := userProfile["id"].(string) - email, _ := userProfile["email"].(string) - firstName, _ := userProfile["first_name"].(string) - lastName, _ := userProfile["last_name"].(string) - name, _ := userProfile["name"].(string) - shortName, _ := userProfile["short_name"].(string) - var picture string - if pictureObj, ok := userProfile["picture"].(map[string]interface{}); ok { - if data, ok := pictureObj["data"].(map[string]interface{}); ok { - if url, ok := data["url"].(string); ok { - picture = url - } - } - } - - authInfo.ProviderUserID = id - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - stdAttrs, err := stdattrs.Extract(map[string]interface{}{ - stdattrs.Email: email, - stdattrs.GivenName: firstName, - stdattrs.FamilyName: lastName, - stdattrs.Name: name, - stdattrs.Nickname: shortName, - stdattrs.Picture: picture, - }, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = stdAttrs - - return -} - -var ( - _ OAuthProvider = &FacebookImpl{} -) diff --git a/pkg/lib/authn/sso/github.go b/pkg/lib/authn/sso/github.go deleted file mode 100644 index 579f56a257..0000000000 --- a/pkg/lib/authn/sso/github.go +++ /dev/null @@ -1,155 +0,0 @@ -package sso - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "strings" - - "github.com/authgear/authgear-server/pkg/api/apierrors" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/util/errorutil" -) - -const ( - githubAuthorizationURL string = "https://github.com/login/oauth/authorize" - // nolint: gosec - githubTokenURL string = "https://github.com/login/oauth/access_token" - githubUserInfoURL string = "https://api.github.com/user" -) - -type GithubImpl struct{} - -func (g *GithubImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity - return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - // ResponseType is unset. - // ResponseMode is unset. - State: param.State, - // Prompt is unset. - // Nonce is unset. - }.Query()), nil -} - -func (g *GithubImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - accessTokenResp, err := g.exchangeCode(deps, param) - if err != nil { - return - } - - userProfile, err := g.fetchUserInfo(deps, accessTokenResp) - if err != nil { - return - } - authInfo.ProviderRawProfile = userProfile - - idJSONNumber, _ := userProfile["id"].(json.Number) - email, _ := userProfile["email"].(string) - login, _ := userProfile["login"].(string) - picture, _ := userProfile["avatar_url"].(string) - profile, _ := userProfile["html_url"].(string) - - id := string(idJSONNumber) - - authInfo.ProviderUserID = id - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - stdAttrs, err := stdattrs.Extract(map[string]interface{}{ - stdattrs.Email: email, - stdattrs.Name: login, - stdattrs.GivenName: login, - stdattrs.Picture: picture, - stdattrs.Profile: profile, - }, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - err = apierrors.AddDetails(err, errorutil.Details{ - "ProviderType": apierrors.APIErrorDetail.Value(deps.ProviderConfig.Type()), - }) - return - } - authInfo.StandardAttributes = stdAttrs - - return -} - -func (g *GithubImpl) exchangeCode(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { - q := make(url.Values) - q.Set("client_id", deps.ProviderConfig.ClientID()) - q.Set("client_secret", deps.ClientSecret) - q.Set("code", param.Code) - q.Set("redirect_uri", param.RedirectURI) - - body := strings.NewReader(q.Encode()) - req, _ := http.NewRequest("POST", githubTokenURL, body) - // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, err := deps.HTTPClient.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - - if resp.StatusCode == 200 { - err = json.NewDecoder(resp.Body).Decode(&accessTokenResp) - if err != nil { - return - } - } else { - var errResp oauthrelyingparty.ErrorResponse - err = json.NewDecoder(resp.Body).Decode(&errResp) - if err != nil { - return - } - err = oauthrelyingpartyutil.ErrorResponseAsError(errResp) - } - - return -} - -func (g *GithubImpl) fetchUserInfo(deps oauthrelyingparty.Dependencies, accessTokenResp oauthrelyingpartyutil.AccessTokenResp) (userProfile map[string]interface{}, err error) { - tokenType := accessTokenResp.TokenType() - accessTokenValue := accessTokenResp.AccessToken() - authorizationHeader := fmt.Sprintf("%s %s", tokenType, accessTokenValue) - - req, err := http.NewRequest(http.MethodGet, githubUserInfoURL, nil) - if err != nil { - return - } - req.Header.Add("Authorization", authorizationHeader) - - resp, err := deps.HTTPClient.Do(req) - if resp != nil { - defer resp.Body.Close() - } - if err != nil { - return - } - - if resp.StatusCode != 200 { - err = fmt.Errorf("failed to fetch user profile: unexpected status code: %d", resp.StatusCode) - return - } - - decoder := json.NewDecoder(resp.Body) - // Deserialize "id" as json.Number. - decoder.UseNumber() - err = decoder.Decode(&userProfile) - if err != nil { - return - } - - return -} - -var ( - _ OAuthProvider = &GithubImpl{} -) diff --git a/pkg/lib/authn/sso/google.go b/pkg/lib/authn/sso/google.go deleted file mode 100644 index cf902f8ccd..0000000000 --- a/pkg/lib/authn/sso/google.go +++ /dev/null @@ -1,123 +0,0 @@ -package sso - -import ( - "context" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" -) - -const ( - googleOIDCDiscoveryDocumentURL string = "https://accounts.google.com/.well-known/openid-configuration" -) - -type GoogleImpl struct{} - -func (f *GoogleImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) - if err != nil { - return "", err - } - return d.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - ResponseMode: param.ResponseMode, - State: param.State, - Nonce: param.Nonce, - Prompt: f.getPrompt(param.Prompt), - }), nil -} - -func (f *GoogleImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) - if err != nil { - return - } - // OPTIMIZE(sso): Cache JWKs - keySet, err := d.FetchJWKs(deps.HTTPClient) - if err != nil { - return - } - - var tokenResp oauthrelyingpartyutil.AccessTokenResp - jwtToken, err := d.ExchangeCode( - deps.HTTPClient, - deps.Clock, - param.Code, - keySet, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - param.RedirectURI, - param.Nonce, - &tokenResp, - ) - if err != nil { - return - } - - claims, err := jwtToken.AsMap(context.TODO()) - if err != nil { - return - } - - // Verify the issuer - // https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken - iss, ok := claims["iss"].(string) - if !ok { - err = OAuthProtocolError.New("iss not found in ID token") - return - } - if iss != "https://accounts.google.com" && iss != "accounts.google.com" { - err = OAuthProtocolError.New("iss is not from Google") - return - } - - // Ensure sub exists - sub, ok := claims["sub"].(string) - if !ok { - err = OAuthProtocolError.New("sub not found in ID token") - return - } - - authInfo.ProviderRawProfile = claims - authInfo.ProviderUserID = sub - // Google supports - // given_name, family_name, email, picture, profile, locale - // https://developers.google.com/identity/protocols/oauth2/openid-connect#obtainuserinfo - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = stdAttrs - - return -} - -func (f *GoogleImpl) getPrompt(prompt []string) []string { - // Google supports `none`, `consent` and `select_account` for prompt. - // The usage of `none` is for checking existing authentication and/or consent - // which doesn't fit auth ui case. - // https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters - newPrompt := []string{} - for _, p := range prompt { - if p == "consent" || - p == "select_account" { - newPrompt = append(newPrompt, p) - } - } - if len(newPrompt) == 0 { - // default - return []string{"select_account"} - } - return newPrompt -} - -var ( - _ OAuthProvider = &GoogleImpl{} -) diff --git a/pkg/lib/authn/sso/linkedin.go b/pkg/lib/authn/sso/linkedin.go deleted file mode 100644 index 3f4fde9a58..0000000000 --- a/pkg/lib/authn/sso/linkedin.go +++ /dev/null @@ -1,335 +0,0 @@ -package sso - -import ( - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" -) - -const ( - linkedinAuthorizationURL string = "https://www.linkedin.com/oauth/v2/authorization" - // nolint: gosec - linkedinTokenURL string = "https://www.linkedin.com/oauth/v2/accessToken" - linkedinMeURL string = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))" - linkedinContactURL string = "https://api.linkedin.com/v2/clientAwareMemberHandles?q=members&projection=(elements*(primary,type,handle~))" -) - -type LinkedInImpl struct{} - -func (f *LinkedInImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - ClientID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - // ResponseMode is unset. - State: param.State, - // Prompt is unset. - // Linkedin doesn't support prompt parameter - // https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS#step-2-request-an-authorization-code - - // Nonce is unset - }.Query()), nil -} - -func (f *LinkedInImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( - deps.HTTPClient, - param.Code, - linkedinTokenURL, - param.RedirectURI, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - ) - if err != nil { - return - } - - meResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinMeURL) - if err != nil { - return - } - - contactResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinContactURL) - if err != nil { - return - } - - // { - // "primary_contact": { - // "elements": [ - // { - // "handle": "urn:li:emailAddress:redacted", - // "handle~": { - // "emailAddress": "user@example.com" - // }, - // "primary": true, - // "type": "EMAIL" - // } - // ] - // }, - // "profile": { - // "id": "redacted", - // "localizedFirstName": "redacted", - // "localizedLastName": "redacted", - // "profilePicture": { - // "displayImage": "urn:li:digitalmediaAsset:redacted", - // "displayImage~": { - // "elements": [ - // { - // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_100_100)", - // "authorizationMethod": "PUBLIC", - // "data": { - // "com.linkedin.digitalmedia.mediaartifact.StillImage": { - // "displayAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "displaySize": { - // "height": 100, - // "uom": "PX", - // "width": 100 - // }, - // "mediaType": "image/jpeg", - // "rawCodecSpec": { - // "name": "jpeg", - // "type": "image" - // }, - // "storageAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "storageSize": { - // "height": 100, - // "width": 100 - // } - // } - // }, - // "identifiers": [ - // { - // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_100_100,0)", - // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_100_100/0/1631684043723?e=1637193600&v=beta&t=h8Wz-EdTjSD9FxQL_oO6hrQ4DdwzGfl5fPPe2cEDPIs", - // "identifierExpiresInSeconds": 1637193600, - // "identifierType": "EXTERNAL_URL", - // "index": 0, - // "mediaType": "image/jpeg" - // } - // ] - // }, - // { - // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_200_200)", - // "authorizationMethod": "PUBLIC", - // "data": { - // "com.linkedin.digitalmedia.mediaartifact.StillImage": { - // "displayAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "displaySize": { - // "height": 200, - // "uom": "PX", - // "width": 200 - // }, - // "mediaType": "image/jpeg", - // "rawCodecSpec": { - // "name": "jpeg", - // "type": "image" - // }, - // "storageAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "storageSize": { - // "height": 200, - // "width": 200 - // } - // } - // }, - // "identifiers": [ - // { - // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_200_200,0)", - // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_200_200/0/1631684043723?e=1637193600&v=beta&t=8CDBMjGCkpk_CO8VgAkVXWeKAu8gYiUTTXPbtMazMUg", - // "identifierExpiresInSeconds": 1637193600, - // "identifierType": "EXTERNAL_URL", - // "index": 0, - // "mediaType": "image/jpeg" - // } - // ] - // }, - // { - // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_400_400)", - // "authorizationMethod": "PUBLIC", - // "data": { - // "com.linkedin.digitalmedia.mediaartifact.StillImage": { - // "displayAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "displaySize": { - // "height": 400, - // "uom": "PX", - // "width": 400 - // }, - // "mediaType": "image/jpeg", - // "rawCodecSpec": { - // "name": "jpeg", - // "type": "image" - // }, - // "storageAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "storageSize": { - // "height": 400, - // "width": 400 - // } - // } - // }, - // "identifiers": [ - // { - // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_400_400,0)", - // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_400_400/0/1631684043723?e=1637193600&v=beta&t=9tCLl0cAbswfKYUgJqDN41QT368cFsq_7TeXyPjixOY", - // "identifierExpiresInSeconds": 1637193600, - // "identifierType": "EXTERNAL_URL", - // "index": 0, - // "mediaType": "image/jpeg" - // } - // ] - // }, - // { - // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_800_800)", - // "authorizationMethod": "PUBLIC", - // "data": { - // "com.linkedin.digitalmedia.mediaartifact.StillImage": { - // "displayAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "displaySize": { - // "height": 800, - // "uom": "PX", - // "width": 800 - // }, - // "mediaType": "image/jpeg", - // "rawCodecSpec": { - // "name": "jpeg", - // "type": "image" - // }, - // "storageAspectRatio": { - // "formatted": "1.00:1.00", - // "heightAspect": 1, - // "widthAspect": 1 - // }, - // "storageSize": { - // "height": 800, - // "width": 800 - // } - // } - // }, - // "identifiers": [ - // { - // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_800_800,0)", - // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_800_800/0/1631684043723?e=1637193600&v=beta&t=hvhZcRfvDrgE64iXNX1J2eWUMAytTtD8SdD006lc3_o", - // "identifierExpiresInSeconds": 1637193600, - // "identifierType": "EXTERNAL_URL", - // "index": 0, - // "mediaType": "image/jpeg" - // } - // ] - // } - // ], - // "paging": { - // "count": 10, - // "links": [], - // "start": 0 - // } - // } - // } - // } - // } - combinedResponse := map[string]interface{}{ - "profile": meResponse, - "primary_contact": contactResponse, - } - - authInfo.ProviderRawProfile = combinedResponse - id, attrs := decodeLinkedIn(combinedResponse) - authInfo.ProviderUserID = id - - emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() - attrs, err = stdattrs.Extract(attrs, stdattrs.ExtractOptions{ - EmailRequired: emailRequired, - }) - if err != nil { - return - } - authInfo.StandardAttributes = attrs - - return -} - -func decodeLinkedIn(userInfo map[string]interface{}) (string, stdattrs.T) { - profile := userInfo["profile"].(map[string]interface{}) - id := profile["id"].(string) - - // Extract email - email := "" - { - primaryContact, _ := userInfo["primary_contact"].(map[string]interface{}) - elements, _ := primaryContact["elements"].([]interface{}) - for _, e := range elements { - element, _ := e.(map[string]interface{}) - if primary, ok := element["primary"].(bool); !ok || !primary { - continue - } - if typ, ok := element["type"].(string); !ok || typ != "EMAIL" { - continue - } - handleTilde, ok := element["handle~"].(map[string]interface{}) - if !ok { - continue - } - email, _ = handleTilde["emailAddress"].(string) - } - } - - // Extract given_name and family_name - firstName, _ := profile["localizedFirstName"].(string) - lastName, _ := profile["localizedLastName"].(string) - - // Extract picture - var picture string - { - profilePicture, _ := profile["profilePicture"].(map[string]interface{}) - displayImage, _ := profilePicture["displayImage~"].(map[string]interface{}) - elements, _ := displayImage["elements"].([]interface{}) - if len(elements) > 0 { - lastElementIface := elements[len(elements)-1] - lastElement, _ := lastElementIface.(map[string]interface{}) - identifiers, _ := lastElement["identifiers"].([]interface{}) - if len(identifiers) > 0 { - firstIdentifierIface := identifiers[0] - firstIdentifier, _ := firstIdentifierIface.(map[string]interface{}) - picture, _ = firstIdentifier["identifier"].(string) - } - } - } - - return id, stdattrs.T{ - stdattrs.Email: email, - stdattrs.GivenName: firstName, - stdattrs.FamilyName: lastName, - stdattrs.Picture: picture, - } -} - -var ( - _ OAuthProvider = &LinkedInImpl{} -) diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index ec81c9c571..405d08f37d 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -5,24 +5,9 @@ import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/clock" ) -// OAuthProvider is OAuth 2.0 based provider. -type OAuthProvider interface { - GetAuthorizationURL(deps oauthrelyingparty.Dependencies, options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) - GetUserProfile(deps oauthrelyingparty.Dependencies, options oauthrelyingparty.GetUserProfileOptions) (oauthrelyingparty.UserProfile, error) -} - type StandardAttributesNormalizer interface { Normalize(stdattrs.T) error } @@ -43,7 +28,7 @@ func (p *OAuthProviderFactory) GetProviderConfig(alias string) (oauthrelyingpart return providerConfig, nil } -func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider, deps *oauthrelyingparty.Dependencies, err error) { +func (p *OAuthProviderFactory) getProvider(alias string) (provider oauthrelyingparty.Provider, deps *oauthrelyingparty.Dependencies, err error) { providerConfig, err := p.GetProviderConfig(alias) if err != nil { return @@ -62,39 +47,8 @@ func (p *OAuthProviderFactory) getProvider(alias string) (provider OAuthProvider HTTPClient: p.HTTPClient.Client, } - switch providerConfig.Type() { - case google.Type: - provider = &GoogleImpl{} - return - case facebook.Type: - provider = &FacebookImpl{} - return - case github.Type: - provider = &GithubImpl{} - return - case linkedin.Type: - provider = &LinkedInImpl{} - return - case azureadv2.Type: - provider = &Azureadv2Impl{} - return - case azureadb2c.Type: - provider = &Azureadb2cImpl{} - return - case adfs.Type: - provider = &ADFSImpl{} - return - case apple.Type: - provider = &AppleImpl{} - return - case wechat.Type: - provider = &WechatImpl{} - return - default: - // TODO(oauth): switch to registry-based resolution. - err = api.ErrOAuthProviderNotFound - return - } + provider = providerConfig.MustGetProvider() + return } func (p *OAuthProviderFactory) GetAuthorizationURL(alias string, options oauthrelyingparty.GetAuthorizationURLOptions) (url string, err error) { diff --git a/pkg/lib/authn/sso/wechat.go b/pkg/lib/authn/sso/wechat.go deleted file mode 100644 index 2cb57264cd..0000000000 --- a/pkg/lib/authn/sso/wechat.go +++ /dev/null @@ -1,259 +0,0 @@ -package sso - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" -) - -const ( - wechatAuthorizationURL = "https://open.weixin.qq.com/connect/oauth2/authorize" - // nolint: gosec - wechatAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token" - wechatUserInfoURL = "https://api.weixin.qq.com/sns/userinfo" -) - -type WechatImpl struct{} - -func (w *WechatImpl) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { - return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ - // ClientID is not used by wechat. - WechatAppID: deps.ProviderConfig.ClientID(), - RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), - ResponseType: oauthrelyingparty.ResponseTypeCode, - // ResponseMode is unset. - State: param.State, - // Prompt is unset. - // Wechat doesn't support prompt parameter - // https://developers.weixin.qq.com/doc/oplatform/en/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html - // Nonce is unset. - }.Query()), nil -} - -func (w *WechatImpl) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { - accessTokenResp, err := wechatFetchAccessTokenResp( - deps.HTTPClient, - param.Code, - deps.ProviderConfig.ClientID(), - deps.ClientSecret, - ) - if err != nil { - return - } - - rawProfile, err := wechatFetchUserProfile(deps.HTTPClient, accessTokenResp) - if err != nil { - return - } - - is_sandbox_account := wechat.ProviderConfig(deps.ProviderConfig).IsSandboxAccount() - var userID string - if is_sandbox_account { - if accessTokenResp.UnionID() != "" { - err = InvalidConfiguration.New("invalid is_sandbox_account config, WeChat sandbox account should not have union id") - return - } - userID = accessTokenResp.OpenID() - } else { - userID = accessTokenResp.UnionID() - } - - if userID == "" { - // this may happen if developer misconfigure is_sandbox_account, e.g. sandbox account doesn't have union id - err = InvalidConfiguration.New("invalid is_sandbox_account config, missing user id in wechat token response") - return - } - - // https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html - // Here is an example of how the raw profile looks like. - // { - // "sex": 0, - // "city": "", - // "openid": "redacted", - // "country": "", - // "language": "zh_CN", - // "nickname": "John Doe", - // "province": "", - // "privilege": [], - // "headimgurl": "" - // } - var gender string - if sex, ok := rawProfile["sex"].(float64); ok { - if sex == 1 { - gender = "male" - } else if sex == 2 { - gender = "female" - } - } - - name, _ := rawProfile["nickname"].(string) - locale, _ := rawProfile["language"].(string) - - authInfo.ProviderRawProfile = rawProfile - authInfo.ProviderUserID = userID - - // Claims.Email.Required is not respected because wechat does not return the email claim. - authInfo.StandardAttributes = stdattrs.T{ - stdattrs.Name: name, - stdattrs.Locale: locale, - stdattrs.Gender: gender, - }.WithNameCopiedToGivenName() - - return -} - -var ( - _ OAuthProvider = &WechatImpl{} -) - -type wechatOAuthErrorResp struct { - ErrorCode int `json:"errcode"` - ErrorMsg string `json:"errmsg"` -} - -func (r *wechatOAuthErrorResp) AsError() error { - return fmt.Errorf("wechat: %d: %s", r.ErrorCode, r.ErrorMsg) -} - -type wechatAccessTokenResp map[string]interface{} - -func (r wechatAccessTokenResp) AccessToken() string { - accessToken, ok := r["access_token"].(string) - if ok { - return accessToken - } - return "" -} - -func (r wechatAccessTokenResp) OpenID() string { - openid, ok := r["openid"].(string) - if ok { - return openid - } - return "" -} - -func (r wechatAccessTokenResp) UnionID() string { - unionid, ok := r["unionid"].(string) - if ok { - return unionid - } - return "" -} - -type wechatUserInfoResp map[string]interface{} - -func (r wechatUserInfoResp) OpenID() string { - openid, ok := r["openid"].(string) - if ok { - return openid - } - return "" -} - -func wechatFetchAccessTokenResp( - client *http.Client, - code string, - appid string, - secret string, -) (r wechatAccessTokenResp, err error) { - v := url.Values{} - v.Set("grant_type", "authorization_code") - v.Add("code", code) - v.Add("appid", appid) - v.Add("secret", secret) - - resp, err := client.PostForm(wechatAccessTokenURL, v) - if resp != nil { - defer resp.Body.Close() - } - - if err != nil { - return - } - - // wechat always return 200 - // to know if there is error, we need to parse the response body - if resp.StatusCode != 200 { - err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) - return - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - err = json.NewDecoder(bytes.NewReader(body)).Decode(&r) - if err != nil { - return - } - if r.AccessToken() == "" { - // failed to obtain access token, parse the error response - var errResp wechatOAuthErrorResp - err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) - if err != nil { - return - } - err = errResp.AsError() - return - } - return -} - -func wechatFetchUserProfile( - client *http.Client, - accessTokenResp wechatAccessTokenResp, -) (userProfile wechatUserInfoResp, err error) { - v := url.Values{} - v.Set("openid", accessTokenResp.OpenID()) - v.Set("access_token", accessTokenResp.AccessToken()) - v.Set("lang", "en") - - resp, err := client.PostForm(wechatUserInfoURL, v) - if resp != nil { - defer resp.Body.Close() - } - - if err != nil { - return - } - - // wechat always return 200 - // to know if there is error, we need to parse the response body - if resp.StatusCode != 200 { - err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) - return - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - err = json.NewDecoder(bytes.NewReader(body)).Decode(&userProfile) - if err != nil { - return - } - if userProfile.OpenID() == "" { - // failed to obtain id from user info, parse the error response - var errResp wechatOAuthErrorResp - err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) - if err != nil { - return - } - err = errResp.AsError() - return - } - - return -} diff --git a/pkg/lib/config/feature_identity.go b/pkg/lib/config/feature_identity.go index 1d73223834..3a80bb438b 100644 --- a/pkg/lib/config/feature_identity.go +++ b/pkg/lib/config/feature_identity.go @@ -2,15 +2,7 @@ package config import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" ) var _ = FeatureConfigSchema.Add("IdentityFeatureConfig", ` @@ -127,23 +119,23 @@ type OAuthSSOProvidersFeatureConfig struct { func (c *OAuthSSOProvidersFeatureConfig) IsDisabled(cfg oauthrelyingparty.ProviderConfig) bool { switch cfg.Type() { - case google.Type: + case liboauthrelyingparty.TypeGoogle: return c.Google.Disabled - case facebook.Type: + case liboauthrelyingparty.TypeFacebook: return c.Facebook.Disabled - case github.Type: + case liboauthrelyingparty.TypeGithub: return c.Github.Disabled - case linkedin.Type: + case liboauthrelyingparty.TypeLinkedin: return c.LinkedIn.Disabled - case azureadv2.Type: + case liboauthrelyingparty.TypeAzureADv2: return c.Azureadv2.Disabled - case azureadb2c.Type: + case liboauthrelyingparty.TypeAzureADB2C: return c.Azureadb2c.Disabled - case adfs.Type: + case liboauthrelyingparty.TypeADFS: return c.ADFS.Disabled - case apple.Type: + case liboauthrelyingparty.TypeApple: return c.Apple.Disabled - case wechat.Type: + case liboauthrelyingparty.TypeWechat: return c.Wechat.Disabled default: // Not a provider we recognize here. Allow it. diff --git a/pkg/lib/oauthrelyingparty/adfs/provider.go b/pkg/lib/oauthrelyingparty/adfs/provider.go index 5f68ca9728..a6abdb1776 100644 --- a/pkg/lib/oauthrelyingparty/adfs/provider.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider.go @@ -1,7 +1,10 @@ package adfs import ( + "context" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -72,3 +75,115 @@ func (ADFS) Scope(_ oauthrelyingparty.ProviderConfig) []string { // The supported scopes are observed from a AD FS server. return []string{"openid", "profile", "email"} } + +func (ADFS) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { + endpoint := ProviderConfig(deps.ProviderConfig).DiscoveryDocumentEndpoint() + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) +} + +func (p ADFS) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return "", err + } + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + ResponseMode: param.ResponseMode, + State: param.State, + Prompt: p.getPrompt(param.Prompt), + Nonce: param.Nonce, + }), nil +} + +func (p ADFS) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return + } + + // OPTIMIZE(sso): Cache JWKs + keySet, err := c.FetchJWKs(deps.HTTPClient) + if err != nil { + return + } + + var tokenResp oauthrelyingpartyutil.AccessTokenResp + jwtToken, err := c.ExchangeCode( + deps.HTTPClient, + deps.Clock, + param.Code, + keySet, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + param.RedirectURI, + param.Nonce, + &tokenResp, + ) + if err != nil { + return + } + + claims, err := jwtToken.AsMap(context.TODO()) + if err != nil { + return + } + + sub, ok := claims["sub"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("sub not found in ID token") + return + } + + // The upn claim is documented here. + // https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/configuring-alternate-login-id + upn, ok := claims["upn"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("upn not found in ID token") + return + } + + extracted, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{}) + if err != nil { + return + } + + // Transform upn into preferred_username + if _, ok := extracted[stdattrs.PreferredUsername]; !ok { + extracted[stdattrs.PreferredUsername] = upn + } + // Transform upn into email + if _, ok := extracted[stdattrs.Email]; !ok { + if emailErr := (validation.FormatEmail{}).CheckFormat(upn); emailErr == nil { + // upn looks like an email address. + extracted[stdattrs.Email] = upn + } + } + + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + extracted, err = stdattrs.Extract(extracted, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = extracted + + authInfo.ProviderRawProfile = claims + authInfo.ProviderUserID = sub + + return +} + +func (ADFS) getPrompt(prompt []string) []string { + // ADFS only supports prompt=login + // https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/ad-fs-prompt-login + for _, p := range prompt { + if p == "login" { + return []string{"login"} + } + } + return []string{} +} diff --git a/pkg/lib/authn/sso/adfs_test.go b/pkg/lib/oauthrelyingparty/adfs/provider_test.go similarity index 85% rename from pkg/lib/authn/sso/adfs_test.go rename to pkg/lib/oauthrelyingparty/adfs/provider_test.go index 9194aa6648..b767fd3ab0 100644 --- a/pkg/lib/authn/sso/adfs_test.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider_test.go @@ -1,4 +1,4 @@ -package sso +package adfs import ( "net/http" @@ -8,11 +8,10 @@ import ( "gopkg.in/h2non/gock.v1" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" ) -func TestADFSImpl(t *testing.T) { - Convey("ADFSImpl", t, func() { +func TestADFS(t *testing.T) { + Convey("ADFS", t, func() { client := &http.Client{} gock.InterceptClient(client) defer gock.Off() @@ -20,13 +19,13 @@ func TestADFSImpl(t *testing.T) { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": adfs.Type, + "type": Type, "discovery_document_endpoint": "https://localhost/.well-known/openid-configuration", }, HTTPClient: client, } - g := &ADFSImpl{} + g := ADFS{} gock.New("https://localhost/.well-known/openid-configuration"). Reply(200). diff --git a/pkg/lib/oauthrelyingparty/apple/provider.go b/pkg/lib/oauthrelyingparty/apple/provider.go index 94f55db4ed..daa3554ea8 100644 --- a/pkg/lib/oauthrelyingparty/apple/provider.go +++ b/pkg/lib/oauthrelyingparty/apple/provider.go @@ -1,9 +1,20 @@ package apple import ( + "context" + "strings" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/crypto" + "github.com/authgear/authgear-server/pkg/util/duration" + "github.com/authgear/authgear-server/pkg/util/jwtutil" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -58,6 +69,12 @@ var Schema = validation.NewSimpleSchema(` } `) +var appleOIDCConfig = oauthrelyingpartyutil.OIDCDiscoveryDocument{ + JWKSUri: "https://appleid.apple.com/auth/keys", + TokenEndpoint: "https://appleid.apple.com/auth/token", + AuthorizationEndpoint: "https://appleid.apple.com/auth/authorize", +} + type Apple struct{} func (Apple) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -89,3 +106,127 @@ func (Apple) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms return []string{"name", "email"} } + +func (Apple) createClientSecret(deps oauthrelyingparty.Dependencies) (clientSecret string, err error) { + teamID := ProviderConfig(deps.ProviderConfig).TeamID() + keyID := ProviderConfig(deps.ProviderConfig).KeyID() + + // https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens + key, err := crypto.ParseAppleP8PrivateKey([]byte(deps.ClientSecret)) + if err != nil { + return + } + + now := deps.Clock.NowUTC() + + payload := jwt.New() + _ = payload.Set(jwt.IssuerKey, teamID) + _ = payload.Set(jwt.IssuedAtKey, now.Unix()) + _ = payload.Set(jwt.ExpirationKey, now.Add(duration.Short).Unix()) + _ = payload.Set(jwt.AudienceKey, "https://appleid.apple.com") + _ = payload.Set(jwt.SubjectKey, deps.ProviderConfig.ClientID) + + jwkKey, err := jwk.FromRaw(key) + if err != nil { + return + } + _ = jwkKey.Set("kid", keyID) + + token, err := jwtutil.Sign(payload, jwa.ES256, jwkKey) + if err != nil { + return + } + + clientSecret = string(token) + return +} + +func (Apple) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + ResponseMode: param.ResponseMode, + State: param.State, + // Prompt is unset. + // Apple doesn't support prompt parameter + // See "Send the Required Query Parameters" section for supporting parameters + // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms + Nonce: param.Nonce, + }), nil +} + +func (p Apple) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + keySet, err := appleOIDCConfig.FetchJWKs(deps.HTTPClient) + if err != nil { + return + } + + clientSecret, err := p.createClientSecret(deps) + if err != nil { + return + } + + var tokenResp oauthrelyingpartyutil.AccessTokenResp + jwtToken, err := appleOIDCConfig.ExchangeCode( + deps.HTTPClient, + deps.Clock, + param.Code, + keySet, + deps.ProviderConfig.ClientID(), + clientSecret, + param.RedirectURI, + param.Nonce, + &tokenResp, + ) + if err != nil { + return + } + + claims, err := jwtToken.AsMap(context.TODO()) + if err != nil { + return + } + + // Verify the issuer + // https://developer.apple.com/documentation/signinwithapplerestapi/verifying_a_user + // The exact spec is + // Verify that the iss field contains https://appleid.apple.com + // Therefore, we use strings.Contains here. + iss, ok := claims["iss"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("iss not found in ID token") + return + } + if !strings.Contains(iss, "https://appleid.apple.com") { + err = oauthrelyingpartyutil.OAuthProtocolError.New("iss does not equal to `https://appleid.apple.com`") + return + } + + // Ensure sub exists + sub, ok := claims["sub"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("sub not found in ID Token") + return + } + + // By observation, if the first time of authentication does NOT include the `name` scope, + // Even the Services ID is unauthorized on https://appleid.apple.com, + // and the `name` scope is included, + // The ID Token still does not include the `name` claim. + + authInfo.ProviderRawProfile = claims + authInfo.ProviderUserID = sub + + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = stdAttrs.WithNameCopiedToGivenName() + + return +} diff --git a/pkg/lib/authn/sso/apple_test.go b/pkg/lib/oauthrelyingparty/apple/provider_test.go similarity index 86% rename from pkg/lib/authn/sso/apple_test.go rename to pkg/lib/oauthrelyingparty/apple/provider_test.go index 38955fa534..0133c8bf87 100644 --- a/pkg/lib/authn/sso/apple_test.go +++ b/pkg/lib/oauthrelyingparty/apple/provider_test.go @@ -1,4 +1,4 @@ -package sso +package apple import ( "testing" @@ -6,7 +6,6 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" ) func TestAppleImpl(t *testing.T) { @@ -14,10 +13,10 @@ func TestAppleImpl(t *testing.T) { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": apple.Type, + "type": Type, }, } - g := &AppleImpl{} + g := Apple{} u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go index bf38e3fb6c..3f76e3dddb 100644 --- a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go @@ -1,7 +1,11 @@ package azureadb2c import ( + "context" + "fmt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -89,3 +93,166 @@ func (AzureADB2C) Scope(_ oauthrelyingparty.ProviderConfig) []string { // If the developer is using User Flow policy, then those claims are called Application Claims. return []string{"openid"} } + +func (AzureADB2C) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { + azureadb2cConfig := ProviderConfig(deps.ProviderConfig) + tenant := azureadb2cConfig.Tenant() + policy := azureadb2cConfig.Policy() + + endpoint := fmt.Sprintf( + "https://%s.b2clogin.com/%s.onmicrosoft.com/%s/v2.0/.well-known/openid-configuration", + tenant, + tenant, + policy, + ) + + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) +} + +func (p AzureADB2C) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return "", err + } + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + ResponseMode: param.ResponseMode, + State: param.State, + Prompt: p.getPrompt(param.Prompt), + Nonce: param.Nonce, + }), nil +} + +func (p AzureADB2C) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return + } + // OPTIMIZE(sso): Cache JWKs + keySet, err := c.FetchJWKs(deps.HTTPClient) + if err != nil { + return + } + + var tokenResp oauthrelyingpartyutil.AccessTokenResp + jwtToken, err := c.ExchangeCode( + deps.HTTPClient, + deps.Clock, + param.Code, + keySet, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + param.RedirectURI, + param.Nonce, + &tokenResp, + ) + if err != nil { + return + } + + claims, err := jwtToken.AsMap(context.TODO()) + if err != nil { + return + } + + iss, ok := claims["iss"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("iss not found in ID token") + return + } + if iss != c.Issuer { + err = oauthrelyingpartyutil.OAuthProtocolError.New( + fmt.Sprintf("iss: %v != %v", iss, c.Issuer), + ) + return + } + + sub, ok := claims["sub"].(string) + if !ok || sub == "" { + err = oauthrelyingpartyutil.OAuthProtocolError.New("sub not found in ID Token") + return + } + + authInfo.ProviderRawProfile = claims + authInfo.ProviderUserID = sub + + stdAttrs, err := p.extract(deps, claims) + if err != nil { + return + } + authInfo.StandardAttributes = stdAttrs + + return +} + +func (AzureADB2C) extract(deps oauthrelyingparty.Dependencies, claims map[string]interface{}) (stdattrs.T, error) { + // Here is the list of possible builtin claims of user flows + // https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#user-flows + // city: free text + // country: free text + // jobTitle: free text + // legalAgeGroupClassification: a enum with undocumented variants + // postalCode: free text + // state: free text + // streetAddress: free text + // newUser: true means the user signed up newly + // oid: sub is identical to it by default. + // emails: if non-empty, the first value corresponds to standard claim + // name: correspond to standard claim + // given_name: correspond to standard claim + // family_name: correspond to standard claim + + // For custom policy we further recognize the following claims. + // https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-profile-attributes + // signInNames.emailAddress: string + + extractString := func(input map[string]interface{}, output stdattrs.T, key string) { + if value, ok := input[key].(string); ok && value != "" { + output[key] = value + } + } + + out := stdattrs.T{} + + extractString(claims, out, stdattrs.Name) + extractString(claims, out, stdattrs.GivenName) + extractString(claims, out, stdattrs.FamilyName) + + var email string + if email == "" { + if ifaceSlice, ok := claims["emails"].([]interface{}); ok { + for _, iface := range ifaceSlice { + if str, ok := iface.(string); ok && str != "" { + email = str + } + } + } + } + if email == "" { + if str, ok := claims["signInNames.emailAddress"].(string); ok { + if str != "" { + email = str + } + } + } + out[stdattrs.Email] = email + + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + return stdattrs.Extract(out, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) +} + +func (AzureADB2C) getPrompt(prompt []string) []string { + // The only supported value is login. + // See https://docs.microsoft.com/en-us/azure/active-directory-b2c/openid-connect + for _, p := range prompt { + if p == "login" { + return []string{"login"} + } + } + return []string{} +} diff --git a/pkg/lib/authn/sso/azureadb2c_test.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go similarity index 89% rename from pkg/lib/authn/sso/azureadb2c_test.go rename to pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go index 6a803a3266..5952bfe644 100644 --- a/pkg/lib/authn/sso/azureadb2c_test.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go @@ -1,4 +1,4 @@ -package sso +package azureadb2c import ( "net/http" @@ -8,7 +8,6 @@ import ( "gopkg.in/h2non/gock.v1" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" ) func TestAzureadb2cImpl(t *testing.T) { @@ -20,14 +19,14 @@ func TestAzureadb2cImpl(t *testing.T) { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": azureadb2c.Type, + "type": Type, "tenant": "tenant", "policy": "policy", }, HTTPClient: client, } - g := &Azureadb2cImpl{} + g := AzureADB2C{} gock.New("https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/v2.0/.well-known/openid-configuration"). Reply(200). diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider.go b/pkg/lib/oauthrelyingparty/azureadv2/provider.go index 31eb995ab7..8d137f55de 100644 --- a/pkg/lib/oauthrelyingparty/azureadv2/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider.go @@ -1,7 +1,11 @@ package azureadv2 import ( + "context" + "fmt" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -84,3 +88,141 @@ func (AzureADv2) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes return []string{"openid", "profile", "email"} } + +func (AzureADv2) getOpenIDConfiguration(deps oauthrelyingparty.Dependencies) (*oauthrelyingpartyutil.OIDCDiscoveryDocument, error) { + // OPTIMIZE(sso): Cache OpenID configuration + + tenant := ProviderConfig(deps.ProviderConfig).Tenant() + + var endpoint string + // Azure special tenant + // + // If the azure tenant is `organizations` or `common`, + // the developer should make use of `before_user_create` and `before_identity_create` hook + // to disallow any undesire identity. + // The `raw_profile` of the identity is the ID Token claims. + // Refer to https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens + // to see what claims the token could contain. + // + // For `organizations`, the user can be any user of any organizational AD. + // Therefore the developer should have a whitelist of AD tenant IDs. + // In the incoming hook, check if `tid` matches one of the entry of the whitelist. + // + // For `common`, in addition to the users from `organizations`, any Microsoft personal account + // could be the user. + // In case of personal account, the `tid` is "9188040d-6c67-4c5b-b112-36a304b66dad". + // Therefore the developer should first check if `tid` indicates personal account. + // If yes, apply their logic to disallow the user creation. + // One very common example is to look at the claim `email`. + // Use a email address parser to parse the email address. + // Obtain the domain and check if the domain is whitelisted. + // For example, if the developer only wants user from hotmail.com to create user, + // ensure `tid` is "9188040d-6c67-4c5b-b112-36a304b66dad" and ensure `email` + // is of domain `@hotmail.com`. + + // As of 2019-09-23, two special values are observed. + // To discover these values, create a new client + // and try different options. + switch tenant { + // Special value for any organizational AD + case "organizations": + endpoint = "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration" + // Special value for any organizational AD and personal accounts (Xbox etc) + case "common": + endpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration" + default: + endpoint = fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0/.well-known/openid-configuration", tenant) + } + + return oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, endpoint) +} + +func (p AzureADv2) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return "", err + } + return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + ResponseMode: param.ResponseMode, + State: param.State, + Prompt: p.getPrompt(param.Prompt), + Nonce: param.Nonce, + }), nil +} + +func (p AzureADv2) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + c, err := p.getOpenIDConfiguration(deps) + if err != nil { + return + } + // OPTIMIZE(sso): Cache JWKs + keySet, err := c.FetchJWKs(deps.HTTPClient) + if err != nil { + return + } + + var tokenResp oauthrelyingpartyutil.AccessTokenResp + jwtToken, err := c.ExchangeCode( + deps.HTTPClient, + deps.Clock, + param.Code, + keySet, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + param.RedirectURI, + param.Nonce, + &tokenResp, + ) + if err != nil { + return + } + + claims, err := jwtToken.AsMap(context.TODO()) + if err != nil { + return + } + + oid, ok := claims["oid"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("oid not found in ID Token") + return + } + // For "Microsoft Account", email usually exists. + // For "AD guest user", email usually exists because to invite an user, the inviter must provide email. + // For "AD user", email never exists even one is provided in "Authentication Methods". + + authInfo.ProviderRawProfile = claims + authInfo.ProviderUserID = oid + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = stdAttrs + + return +} + +func (AzureADv2) getPrompt(prompt []string) []string { + // Azureadv2 only supports single value for prompt. + // The first supporting value in the list will be used. + // The usage of `none` is for checking existing authentication and/or consent + // which doesn't fit auth ui case. + // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow + for _, p := range prompt { + if p == "login" { + return []string{"login"} + } else if p == "consent" { + return []string{"consent"} + } else if p == "select_account" { + return []string{"select_account"} + } + } + return []string{} +} diff --git a/pkg/lib/authn/sso/azureadv2_test.go b/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go similarity index 95% rename from pkg/lib/authn/sso/azureadv2_test.go rename to pkg/lib/oauthrelyingparty/azureadv2/provider_test.go index b5394b0831..fc9f20f9d7 100644 --- a/pkg/lib/authn/sso/azureadv2_test.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go @@ -1,4 +1,4 @@ -package sso +package azureadv2 import ( "net/http" @@ -8,7 +8,6 @@ import ( "gopkg.in/h2non/gock.v1" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" ) func TestAzureadv2Impl(t *testing.T) { @@ -20,13 +19,13 @@ func TestAzureadv2Impl(t *testing.T) { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": azureadv2.Type, + "type": Type, "tenant": "common", }, HTTPClient: client, } - g := &Azureadv2Impl{} + g := AzureADv2{} gock.New("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"). Reply(200). diff --git a/pkg/lib/oauthrelyingparty/facebook/provider.go b/pkg/lib/oauthrelyingparty/facebook/provider.go index e702ffec5c..0d5de10c3b 100644 --- a/pkg/lib/oauthrelyingparty/facebook/provider.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider.go @@ -1,9 +1,13 @@ package facebook import ( + "net/url" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -44,6 +48,13 @@ var Schema = validation.NewSimpleSchema(` } `) +const ( + facebookAuthorizationURL string = "https://www.facebook.com/v11.0/dialog/oauth" + // nolint: gosec + facebookTokenURL string = "https://graph.facebook.com/v11.0/oauth/access_token" + facebookUserInfoURL string = "https://graph.facebook.com/v11.0/me?fields=id,email,first_name,last_name,middle_name,name,name_format,picture,short_name" +) + type Facebook struct{} func (Facebook) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -72,3 +83,103 @@ func (Facebook) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://developers.facebook.com/docs/permissions/reference return []string{"email", "public_profile"} } + +func (Facebook) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + // ResponseMode is unset + State: param.State, + // Prompt is unset. + // Facebook doesn't support prompt parameter + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/ + + // Nonce is unset + }.Query()), nil +} + +func (Facebook) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + authInfo = oauthrelyingparty.UserProfile{} + + accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( + deps.HTTPClient, + param.Code, + facebookTokenURL, + param.RedirectURI, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + ) + if err != nil { + return + } + + userProfileURL, err := url.Parse(facebookUserInfoURL) + if err != nil { + return + } + q := userProfileURL.Query() + appSecretProof := crypto.HMACSHA256String([]byte(deps.ClientSecret), []byte(accessTokenResp.AccessToken())) + q.Set("appsecret_proof", appSecretProof) + userProfileURL.RawQuery = q.Encode() + + // Here is the refacted user profile of Louis' facebook account. + // { + // "id": "redacted", + // "email": "redacted", + // "first_name": "Jonathan", + // "last_name": "Doe", + // "name": "Johnathan Doe", + // "name_format": "{first} {last}", + // "picture": { + // "data": { + // "height": 50, + // "is_silhouette": true, + // "url": "http://example.com", + // "width": 50 + // } + // }, + // "short_name": "John" + // } + + userProfile, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, userProfileURL.String()) + if err != nil { + return + } + authInfo.ProviderRawProfile = userProfile + + id, _ := userProfile["id"].(string) + email, _ := userProfile["email"].(string) + firstName, _ := userProfile["first_name"].(string) + lastName, _ := userProfile["last_name"].(string) + name, _ := userProfile["name"].(string) + shortName, _ := userProfile["short_name"].(string) + var picture string + if pictureObj, ok := userProfile["picture"].(map[string]interface{}); ok { + if data, ok := pictureObj["data"].(map[string]interface{}); ok { + if url, ok := data["url"].(string); ok { + picture = url + } + } + } + + authInfo.ProviderUserID = id + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + stdAttrs, err := stdattrs.Extract(map[string]interface{}{ + stdattrs.Email: email, + stdattrs.GivenName: firstName, + stdattrs.FamilyName: lastName, + stdattrs.Name: name, + stdattrs.Nickname: shortName, + stdattrs.Picture: picture, + }, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = stdAttrs + + return +} diff --git a/pkg/lib/authn/sso/facebook_test.go b/pkg/lib/oauthrelyingparty/facebook/provider_test.go similarity index 76% rename from pkg/lib/authn/sso/facebook_test.go rename to pkg/lib/oauthrelyingparty/facebook/provider_test.go index 4b9892f2b5..b81fbcca0e 100644 --- a/pkg/lib/authn/sso/facebook_test.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider_test.go @@ -1,4 +1,4 @@ -package sso +package facebook import ( "testing" @@ -6,18 +6,17 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" ) -func TestFacebookImpl(t *testing.T) { - Convey("FacebookImpl", t, func() { +func TestFacebook(t *testing.T) { + Convey("Facebook", t, func() { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": facebook.Type, + "type": Type, }, } - g := &FacebookImpl{} + g := Facebook{} u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", diff --git a/pkg/lib/oauthrelyingparty/github/provider.go b/pkg/lib/oauthrelyingparty/github/provider.go index a6960a9783..7d2c72275b 100644 --- a/pkg/lib/oauthrelyingparty/github/provider.go +++ b/pkg/lib/oauthrelyingparty/github/provider.go @@ -1,9 +1,18 @@ package github import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" + "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -44,6 +53,13 @@ var Schema = validation.NewSimpleSchema(` } `) +const ( + githubAuthorizationURL string = "https://github.com/login/oauth/authorize" + // nolint: gosec + githubTokenURL string = "https://github.com/login/oauth/access_token" + githubUserInfoURL string = "https://api.github.com/user" +) + type Github struct{} func (Github) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -65,3 +81,130 @@ func (Github) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps return []string{"read:user", "user:email"} } + +func (Github) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity + return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + // ResponseType is unset. + // ResponseMode is unset. + State: param.State, + // Prompt is unset. + // Nonce is unset. + }.Query()), nil +} + +func (p Github) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + accessTokenResp, err := p.exchangeCode(deps, param) + if err != nil { + return + } + + userProfile, err := p.fetchUserInfo(deps, accessTokenResp) + if err != nil { + return + } + authInfo.ProviderRawProfile = userProfile + + idJSONNumber, _ := userProfile["id"].(json.Number) + email, _ := userProfile["email"].(string) + login, _ := userProfile["login"].(string) + picture, _ := userProfile["avatar_url"].(string) + profile, _ := userProfile["html_url"].(string) + + id := string(idJSONNumber) + + authInfo.ProviderUserID = id + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + stdAttrs, err := stdattrs.Extract(map[string]interface{}{ + stdattrs.Email: email, + stdattrs.Name: login, + stdattrs.GivenName: login, + stdattrs.Picture: picture, + stdattrs.Profile: profile, + }, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + err = apierrors.AddDetails(err, errorutil.Details{ + "ProviderType": apierrors.APIErrorDetail.Value(deps.ProviderConfig.Type()), + }) + return + } + authInfo.StandardAttributes = stdAttrs + + return +} + +func (Github) exchangeCode(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (accessTokenResp oauthrelyingpartyutil.AccessTokenResp, err error) { + q := make(url.Values) + q.Set("client_id", deps.ProviderConfig.ClientID()) + q.Set("client_secret", deps.ClientSecret) + q.Set("code", param.Code) + q.Set("redirect_uri", param.RedirectURI) + + body := strings.NewReader(q.Encode()) + req, _ := http.NewRequest("POST", githubTokenURL, body) + // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := deps.HTTPClient.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + if resp.StatusCode == 200 { + err = json.NewDecoder(resp.Body).Decode(&accessTokenResp) + if err != nil { + return + } + } else { + var errResp oauthrelyingparty.ErrorResponse + err = json.NewDecoder(resp.Body).Decode(&errResp) + if err != nil { + return + } + err = oauthrelyingpartyutil.ErrorResponseAsError(errResp) + } + + return +} + +func (Github) fetchUserInfo(deps oauthrelyingparty.Dependencies, accessTokenResp oauthrelyingpartyutil.AccessTokenResp) (userProfile map[string]interface{}, err error) { + tokenType := accessTokenResp.TokenType() + accessTokenValue := accessTokenResp.AccessToken() + authorizationHeader := fmt.Sprintf("%s %s", tokenType, accessTokenValue) + + req, err := http.NewRequest(http.MethodGet, githubUserInfoURL, nil) + if err != nil { + return + } + req.Header.Add("Authorization", authorizationHeader) + + resp, err := deps.HTTPClient.Do(req) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + return + } + + if resp.StatusCode != 200 { + err = fmt.Errorf("failed to fetch user profile: unexpected status code: %d", resp.StatusCode) + return + } + + decoder := json.NewDecoder(resp.Body) + // Deserialize "id" as json.Number. + decoder.UseNumber() + err = decoder.Decode(&userProfile) + if err != nil { + return + } + + return +} diff --git a/pkg/lib/authn/sso/github_test.go b/pkg/lib/oauthrelyingparty/github/provider_test.go similarity index 76% rename from pkg/lib/authn/sso/github_test.go rename to pkg/lib/oauthrelyingparty/github/provider_test.go index 2ae2cb23d2..e5861f14fa 100644 --- a/pkg/lib/authn/sso/github_test.go +++ b/pkg/lib/oauthrelyingparty/github/provider_test.go @@ -1,4 +1,4 @@ -package sso +package github import ( "testing" @@ -6,18 +6,17 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" ) -func TestGithubImpl(t *testing.T) { - Convey("GithubImpl", t, func() { +func TestGithub(t *testing.T) { + Convey("Github", t, func() { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": github.Type, + "type": Type, }, } - g := &GithubImpl{} + g := Github{} u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", diff --git a/pkg/lib/oauthrelyingparty/google/provider.go b/pkg/lib/oauthrelyingparty/google/provider.go index 38c805b91a..8474ce9bbe 100644 --- a/pkg/lib/oauthrelyingparty/google/provider.go +++ b/pkg/lib/oauthrelyingparty/google/provider.go @@ -1,7 +1,10 @@ package google import ( + "context" + "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -44,6 +47,10 @@ var Schema = validation.NewSimpleSchema(` } `) +const ( + googleOIDCDiscoveryDocumentURL string = "https://accounts.google.com/.well-known/openid-configuration" +) + type Google struct{} func (Google) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -68,3 +75,107 @@ func (Google) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://developers.google.com/identity/protocols/oauth2/openid-connect return []string{"openid", "profile", "email"} } + +func (p Google) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) + if err != nil { + return "", err + } + return d.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + ResponseMode: param.ResponseMode, + State: param.State, + Nonce: param.Nonce, + Prompt: p.getPrompt(param.Prompt), + }), nil +} + +func (Google) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + d, err := oauthrelyingpartyutil.FetchOIDCDiscoveryDocument(deps.HTTPClient, googleOIDCDiscoveryDocumentURL) + if err != nil { + return + } + // OPTIMIZE(sso): Cache JWKs + keySet, err := d.FetchJWKs(deps.HTTPClient) + if err != nil { + return + } + + var tokenResp oauthrelyingpartyutil.AccessTokenResp + jwtToken, err := d.ExchangeCode( + deps.HTTPClient, + deps.Clock, + param.Code, + keySet, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + param.RedirectURI, + param.Nonce, + &tokenResp, + ) + if err != nil { + return + } + + claims, err := jwtToken.AsMap(context.TODO()) + if err != nil { + return + } + + // Verify the issuer + // https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken + iss, ok := claims["iss"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("iss not found in ID token") + return + } + if iss != "https://accounts.google.com" && iss != "accounts.google.com" { + err = oauthrelyingpartyutil.OAuthProtocolError.New("iss is not from Google") + return + } + + // Ensure sub exists + sub, ok := claims["sub"].(string) + if !ok { + err = oauthrelyingpartyutil.OAuthProtocolError.New("sub not found in ID token") + return + } + + authInfo.ProviderRawProfile = claims + authInfo.ProviderUserID = sub + // Google supports + // given_name, family_name, email, picture, profile, locale + // https://developers.google.com/identity/protocols/oauth2/openid-connect#obtainuserinfo + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + stdAttrs, err := stdattrs.Extract(claims, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = stdAttrs + + return +} + +func (Google) getPrompt(prompt []string) []string { + // Google supports `none`, `consent` and `select_account` for prompt. + // The usage of `none` is for checking existing authentication and/or consent + // which doesn't fit auth ui case. + // https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters + newPrompt := []string{} + for _, p := range prompt { + if p == "consent" || + p == "select_account" { + newPrompt = append(newPrompt, p) + } + } + if len(newPrompt) == 0 { + // default + return []string{"select_account"} + } + return newPrompt +} diff --git a/pkg/lib/authn/sso/google_test.go b/pkg/lib/oauthrelyingparty/google/provider_test.go similarity index 94% rename from pkg/lib/authn/sso/google_test.go rename to pkg/lib/oauthrelyingparty/google/provider_test.go index a511d34c6e..3a9413f4dc 100644 --- a/pkg/lib/authn/sso/google_test.go +++ b/pkg/lib/oauthrelyingparty/google/provider_test.go @@ -1,4 +1,4 @@ -package sso +package google import ( "net/http" @@ -8,7 +8,6 @@ import ( "gopkg.in/h2non/gock.v1" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGoogleImpl(t *testing.T) { @@ -20,12 +19,12 @@ func TestGoogleImpl(t *testing.T) { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": google.Type, + "type": Type, }, HTTPClient: client, } - g := &GoogleImpl{} + g := Google{} gock.New(googleOIDCDiscoveryDocumentURL). Reply(200). diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider.go b/pkg/lib/oauthrelyingparty/linkedin/provider.go index c1591fb4d1..dc989be162 100644 --- a/pkg/lib/oauthrelyingparty/linkedin/provider.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider.go @@ -2,6 +2,7 @@ package linkedin import ( "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -44,6 +45,14 @@ var Schema = validation.NewSimpleSchema(` } `) +const ( + linkedinAuthorizationURL string = "https://www.linkedin.com/oauth/v2/authorization" + // nolint: gosec + linkedinTokenURL string = "https://www.linkedin.com/oauth/v2/accessToken" + linkedinMeURL string = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))" + linkedinContactURL string = "https://api.linkedin.com/v2/clientAwareMemberHandles?q=members&projection=(elements*(primary,type,handle~))" +) + type Linkedin struct{} func (Linkedin) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -72,3 +81,317 @@ func (Linkedin) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/primary-contact-api?context=linkedin/compliance/context return []string{"r_liteprofile", "r_emailaddress"} } + +func (Linkedin) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ + ClientID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + // ResponseMode is unset. + State: param.State, + // Prompt is unset. + // Linkedin doesn't support prompt parameter + // https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?tabs=HTTPS#step-2-request-an-authorization-code + + // Nonce is unset + }.Query()), nil +} + +func (Linkedin) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + accessTokenResp, err := oauthrelyingpartyutil.FetchAccessTokenResp( + deps.HTTPClient, + param.Code, + linkedinTokenURL, + param.RedirectURI, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + ) + if err != nil { + return + } + + meResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinMeURL) + if err != nil { + return + } + + contactResponse, err := oauthrelyingpartyutil.FetchUserProfile(deps.HTTPClient, accessTokenResp, linkedinContactURL) + if err != nil { + return + } + + // { + // "primary_contact": { + // "elements": [ + // { + // "handle": "urn:li:emailAddress:redacted", + // "handle~": { + // "emailAddress": "user@example.com" + // }, + // "primary": true, + // "type": "EMAIL" + // } + // ] + // }, + // "profile": { + // "id": "redacted", + // "localizedFirstName": "redacted", + // "localizedLastName": "redacted", + // "profilePicture": { + // "displayImage": "urn:li:digitalmediaAsset:redacted", + // "displayImage~": { + // "elements": [ + // { + // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_100_100)", + // "authorizationMethod": "PUBLIC", + // "data": { + // "com.linkedin.digitalmedia.mediaartifact.StillImage": { + // "displayAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "displaySize": { + // "height": 100, + // "uom": "PX", + // "width": 100 + // }, + // "mediaType": "image/jpeg", + // "rawCodecSpec": { + // "name": "jpeg", + // "type": "image" + // }, + // "storageAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "storageSize": { + // "height": 100, + // "width": 100 + // } + // } + // }, + // "identifiers": [ + // { + // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_100_100,0)", + // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_100_100/0/1631684043723?e=1637193600&v=beta&t=h8Wz-EdTjSD9FxQL_oO6hrQ4DdwzGfl5fPPe2cEDPIs", + // "identifierExpiresInSeconds": 1637193600, + // "identifierType": "EXTERNAL_URL", + // "index": 0, + // "mediaType": "image/jpeg" + // } + // ] + // }, + // { + // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_200_200)", + // "authorizationMethod": "PUBLIC", + // "data": { + // "com.linkedin.digitalmedia.mediaartifact.StillImage": { + // "displayAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "displaySize": { + // "height": 200, + // "uom": "PX", + // "width": 200 + // }, + // "mediaType": "image/jpeg", + // "rawCodecSpec": { + // "name": "jpeg", + // "type": "image" + // }, + // "storageAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "storageSize": { + // "height": 200, + // "width": 200 + // } + // } + // }, + // "identifiers": [ + // { + // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_200_200,0)", + // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_200_200/0/1631684043723?e=1637193600&v=beta&t=8CDBMjGCkpk_CO8VgAkVXWeKAu8gYiUTTXPbtMazMUg", + // "identifierExpiresInSeconds": 1637193600, + // "identifierType": "EXTERNAL_URL", + // "index": 0, + // "mediaType": "image/jpeg" + // } + // ] + // }, + // { + // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_400_400)", + // "authorizationMethod": "PUBLIC", + // "data": { + // "com.linkedin.digitalmedia.mediaartifact.StillImage": { + // "displayAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "displaySize": { + // "height": 400, + // "uom": "PX", + // "width": 400 + // }, + // "mediaType": "image/jpeg", + // "rawCodecSpec": { + // "name": "jpeg", + // "type": "image" + // }, + // "storageAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "storageSize": { + // "height": 400, + // "width": 400 + // } + // } + // }, + // "identifiers": [ + // { + // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_400_400,0)", + // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_400_400/0/1631684043723?e=1637193600&v=beta&t=9tCLl0cAbswfKYUgJqDN41QT368cFsq_7TeXyPjixOY", + // "identifierExpiresInSeconds": 1637193600, + // "identifierType": "EXTERNAL_URL", + // "index": 0, + // "mediaType": "image/jpeg" + // } + // ] + // }, + // { + // "artifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_800_800)", + // "authorizationMethod": "PUBLIC", + // "data": { + // "com.linkedin.digitalmedia.mediaartifact.StillImage": { + // "displayAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "displaySize": { + // "height": 800, + // "uom": "PX", + // "width": 800 + // }, + // "mediaType": "image/jpeg", + // "rawCodecSpec": { + // "name": "jpeg", + // "type": "image" + // }, + // "storageAspectRatio": { + // "formatted": "1.00:1.00", + // "heightAspect": 1, + // "widthAspect": 1 + // }, + // "storageSize": { + // "height": 800, + // "width": 800 + // } + // } + // }, + // "identifiers": [ + // { + // "file": "urn:li:digitalmediaFile:(urn:li:digitalmediaAsset:C5603AQE9WylLgWcyuA,urn:li:digitalmediaMediaArtifactClass:profile-displayphoto-shrink_800_800,0)", + // "identifier": "https://media-exp1.licdn.com/dms/image/C5603AQE9WylLgWcyuA/profile-displayphoto-shrink_800_800/0/1631684043723?e=1637193600&v=beta&t=hvhZcRfvDrgE64iXNX1J2eWUMAytTtD8SdD006lc3_o", + // "identifierExpiresInSeconds": 1637193600, + // "identifierType": "EXTERNAL_URL", + // "index": 0, + // "mediaType": "image/jpeg" + // } + // ] + // } + // ], + // "paging": { + // "count": 10, + // "links": [], + // "start": 0 + // } + // } + // } + // } + // } + combinedResponse := map[string]interface{}{ + "profile": meResponse, + "primary_contact": contactResponse, + } + + authInfo.ProviderRawProfile = combinedResponse + id, attrs := decodeLinkedIn(combinedResponse) + authInfo.ProviderUserID = id + + emailRequired := deps.ProviderConfig.EmailClaimConfig().Required() + attrs, err = stdattrs.Extract(attrs, stdattrs.ExtractOptions{ + EmailRequired: emailRequired, + }) + if err != nil { + return + } + authInfo.StandardAttributes = attrs + + return +} + +func decodeLinkedIn(userInfo map[string]interface{}) (string, stdattrs.T) { + profile := userInfo["profile"].(map[string]interface{}) + id := profile["id"].(string) + + // Extract email + email := "" + { + primaryContact, _ := userInfo["primary_contact"].(map[string]interface{}) + elements, _ := primaryContact["elements"].([]interface{}) + for _, e := range elements { + element, _ := e.(map[string]interface{}) + if primary, ok := element["primary"].(bool); !ok || !primary { + continue + } + if typ, ok := element["type"].(string); !ok || typ != "EMAIL" { + continue + } + handleTilde, ok := element["handle~"].(map[string]interface{}) + if !ok { + continue + } + email, _ = handleTilde["emailAddress"].(string) + } + } + + // Extract given_name and family_name + firstName, _ := profile["localizedFirstName"].(string) + lastName, _ := profile["localizedLastName"].(string) + + // Extract picture + var picture string + { + profilePicture, _ := profile["profilePicture"].(map[string]interface{}) + displayImage, _ := profilePicture["displayImage~"].(map[string]interface{}) + elements, _ := displayImage["elements"].([]interface{}) + if len(elements) > 0 { + lastElementIface := elements[len(elements)-1] + lastElement, _ := lastElementIface.(map[string]interface{}) + identifiers, _ := lastElement["identifiers"].([]interface{}) + if len(identifiers) > 0 { + firstIdentifierIface := identifiers[0] + firstIdentifier, _ := firstIdentifierIface.(map[string]interface{}) + picture, _ = firstIdentifier["identifier"].(string) + } + } + } + + return id, stdattrs.T{ + stdattrs.Email: email, + stdattrs.GivenName: firstName, + stdattrs.FamilyName: lastName, + stdattrs.Picture: picture, + } +} diff --git a/pkg/lib/authn/sso/linkedin_test.go b/pkg/lib/oauthrelyingparty/linkedin/provider_test.go similarity index 76% rename from pkg/lib/authn/sso/linkedin_test.go rename to pkg/lib/oauthrelyingparty/linkedin/provider_test.go index 821f1fc983..75f6a62d69 100644 --- a/pkg/lib/authn/sso/linkedin_test.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider_test.go @@ -1,4 +1,4 @@ -package sso +package linkedin import ( "testing" @@ -6,18 +6,17 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" ) -func TestLinkedInImpl(t *testing.T) { - Convey("LinkedInImpl", t, func() { +func TestLinkedin(t *testing.T) { + Convey("Linkedin", t, func() { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": linkedin.Type, + "type": Type, }, } - g := &LinkedInImpl{} + g := Linkedin{} u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ RedirectURI: "https://localhost/", diff --git a/pkg/lib/oauthrelyingparty/wechat/provider.go b/pkg/lib/oauthrelyingparty/wechat/provider.go index 1b2b6bce5f..1445cd0874 100644 --- a/pkg/lib/oauthrelyingparty/wechat/provider.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider.go @@ -1,9 +1,16 @@ package wechat import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" "strconv" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" @@ -85,6 +92,13 @@ var Schema = validation.NewSimpleSchema(` } `) +const ( + wechatAuthorizationURL = "https://open.weixin.qq.com/connect/oauth2/authorize" + // nolint: gosec + wechatAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token" + wechatUserInfoURL = "https://api.weixin.qq.com/sns/userinfo" +) + type Wechat struct{} func (Wechat) ValidateProviderConfig(ctx *validation.Context, cfg oauthrelyingparty.ProviderConfig) { @@ -117,3 +131,234 @@ func (Wechat) Scope(_ oauthrelyingparty.ProviderConfig) []string { // https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html return []string{"snsapi_userinfo"} } + +func (Wechat) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { + return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ + // ClientID is not used by wechat. + WechatAppID: deps.ProviderConfig.ClientID(), + RedirectURI: param.RedirectURI, + Scope: deps.ProviderConfig.Scope(), + ResponseType: oauthrelyingparty.ResponseTypeCode, + // ResponseMode is unset. + State: param.State, + // Prompt is unset. + // Wechat doesn't support prompt parameter + // https://developers.weixin.qq.com/doc/oplatform/en/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html + // Nonce is unset. + }.Query()), nil +} + +func (Wechat) GetUserProfile(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetUserProfileOptions) (authInfo oauthrelyingparty.UserProfile, err error) { + accessTokenResp, err := wechatFetchAccessTokenResp( + deps.HTTPClient, + param.Code, + deps.ProviderConfig.ClientID(), + deps.ClientSecret, + ) + if err != nil { + return + } + + rawProfile, err := wechatFetchUserProfile(deps.HTTPClient, accessTokenResp) + if err != nil { + return + } + + is_sandbox_account := ProviderConfig(deps.ProviderConfig).IsSandboxAccount() + var userID string + if is_sandbox_account { + if accessTokenResp.UnionID() != "" { + err = oauthrelyingpartyutil.InvalidConfiguration.New("invalid is_sandbox_account config, WeChat sandbox account should not have union id") + return + } + userID = accessTokenResp.OpenID() + } else { + userID = accessTokenResp.UnionID() + } + + if userID == "" { + // this may happen if developer misconfigure is_sandbox_account, e.g. sandbox account doesn't have union id + err = oauthrelyingpartyutil.InvalidConfiguration.New("invalid is_sandbox_account config, missing user id in wechat token response") + return + } + + // https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html + // Here is an example of how the raw profile looks like. + // { + // "sex": 0, + // "city": "", + // "openid": "redacted", + // "country": "", + // "language": "zh_CN", + // "nickname": "John Doe", + // "province": "", + // "privilege": [], + // "headimgurl": "" + // } + var gender string + if sex, ok := rawProfile["sex"].(float64); ok { + if sex == 1 { + gender = "male" + } else if sex == 2 { + gender = "female" + } + } + + name, _ := rawProfile["nickname"].(string) + locale, _ := rawProfile["language"].(string) + + authInfo.ProviderRawProfile = rawProfile + authInfo.ProviderUserID = userID + + // Claims.Email.Required is not respected because wechat does not return the email claim. + authInfo.StandardAttributes = stdattrs.T{ + stdattrs.Name: name, + stdattrs.Locale: locale, + stdattrs.Gender: gender, + }.WithNameCopiedToGivenName() + + return +} + +type wechatOAuthErrorResp struct { + ErrorCode int `json:"errcode"` + ErrorMsg string `json:"errmsg"` +} + +func (r *wechatOAuthErrorResp) AsError() error { + return fmt.Errorf("wechat: %d: %s", r.ErrorCode, r.ErrorMsg) +} + +type wechatAccessTokenResp map[string]interface{} + +func (r wechatAccessTokenResp) AccessToken() string { + accessToken, ok := r["access_token"].(string) + if ok { + return accessToken + } + return "" +} + +func (r wechatAccessTokenResp) OpenID() string { + openid, ok := r["openid"].(string) + if ok { + return openid + } + return "" +} + +func (r wechatAccessTokenResp) UnionID() string { + unionid, ok := r["unionid"].(string) + if ok { + return unionid + } + return "" +} + +type wechatUserInfoResp map[string]interface{} + +func (r wechatUserInfoResp) OpenID() string { + openid, ok := r["openid"].(string) + if ok { + return openid + } + return "" +} + +func wechatFetchAccessTokenResp( + client *http.Client, + code string, + appid string, + secret string, +) (r wechatAccessTokenResp, err error) { + v := url.Values{} + v.Set("grant_type", "authorization_code") + v.Add("code", code) + v.Add("appid", appid) + v.Add("secret", secret) + + resp, err := client.PostForm(wechatAccessTokenURL, v) + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + return + } + + // wechat always return 200 + // to know if there is error, we need to parse the response body + if resp.StatusCode != 200 { + err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) + return + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return + } + + err = json.NewDecoder(bytes.NewReader(body)).Decode(&r) + if err != nil { + return + } + if r.AccessToken() == "" { + // failed to obtain access token, parse the error response + var errResp wechatOAuthErrorResp + err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) + if err != nil { + return + } + err = errResp.AsError() + return + } + return +} + +func wechatFetchUserProfile( + client *http.Client, + accessTokenResp wechatAccessTokenResp, +) (userProfile wechatUserInfoResp, err error) { + v := url.Values{} + v.Set("openid", accessTokenResp.OpenID()) + v.Set("access_token", accessTokenResp.AccessToken()) + v.Set("lang", "en") + + resp, err := client.PostForm(wechatUserInfoURL, v) + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + return + } + + // wechat always return 200 + // to know if there is error, we need to parse the response body + if resp.StatusCode != 200 { + err = fmt.Errorf("wechat: unexpected status code: %d", resp.StatusCode) + return + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return + } + + err = json.NewDecoder(bytes.NewReader(body)).Decode(&userProfile) + if err != nil { + return + } + if userProfile.OpenID() == "" { + // failed to obtain id from user info, parse the error response + var errResp wechatOAuthErrorResp + err = json.NewDecoder(bytes.NewReader(body)).Decode(&errResp) + if err != nil { + return + } + err = errResp.AsError() + return + } + + return +} diff --git a/pkg/lib/authn/sso/wechat_test.go b/pkg/lib/oauthrelyingparty/wechat/provider_test.go similarity index 75% rename from pkg/lib/authn/sso/wechat_test.go rename to pkg/lib/oauthrelyingparty/wechat/provider_test.go index 8e91ff7a23..55818707c6 100644 --- a/pkg/lib/authn/sso/wechat_test.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider_test.go @@ -1,4 +1,4 @@ -package sso +package wechat import ( "testing" @@ -6,19 +6,18 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) -func TestWechatImpl(t *testing.T) { - Convey("WechatImpl", t, func() { +func TestWechat(t *testing.T) { + Convey("Wechat", t, func() { deps := oauthrelyingparty.Dependencies{ ProviderConfig: oauthrelyingparty.ProviderConfig{ "client_id": "client_id", - "type": wechat.Type, + "type": Type, }, } - g := &WechatImpl{} + g := Wechat{} u, err := g.GetAuthorizationURL(deps, oauthrelyingparty.GetAuthorizationURLOptions{ Nonce: "nonce", From 1e4cb0675fecfa665abfe070e86827db40f840ff Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 18:52:56 +0800 Subject: [PATCH 22/30] Import provider in tests So that they are registered. --- .../declarative/generate_config_login_flow_test.go | 1 + .../declarative/generate_config_reauth_flow_test.go | 1 + .../declarative/generate_config_signup_flow_test.go | 1 + .../declarative/generate_config_signup_login_flow_test.go | 1 + pkg/lib/config/config_test.go | 8 ++++++++ pkg/lib/config/configsource/resources_test.go | 2 ++ pkg/lib/config/secret_test.go | 1 + pkg/lib/tutorial/service_test.go | 1 + 8 files changed, 16 insertions(+) diff --git a/pkg/lib/authenticationflow/declarative/generate_config_login_flow_test.go b/pkg/lib/authenticationflow/declarative/generate_config_login_flow_test.go index e598f5b6b7..07f7418804 100644 --- a/pkg/lib/authenticationflow/declarative/generate_config_login_flow_test.go +++ b/pkg/lib/authenticationflow/declarative/generate_config_login_flow_test.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGenerateLoginFlowConfig(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/generate_config_reauth_flow_test.go b/pkg/lib/authenticationflow/declarative/generate_config_reauth_flow_test.go index 261b8ed8cd..da3bcf6f57 100644 --- a/pkg/lib/authenticationflow/declarative/generate_config_reauth_flow_test.go +++ b/pkg/lib/authenticationflow/declarative/generate_config_reauth_flow_test.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGenerateReauthFlowConfig(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/generate_config_signup_flow_test.go b/pkg/lib/authenticationflow/declarative/generate_config_signup_flow_test.go index d1233eaddf..98187591d4 100644 --- a/pkg/lib/authenticationflow/declarative/generate_config_signup_flow_test.go +++ b/pkg/lib/authenticationflow/declarative/generate_config_signup_flow_test.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGenerateSignupFlowConfig(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/generate_config_signup_login_flow_test.go b/pkg/lib/authenticationflow/declarative/generate_config_signup_login_flow_test.go index 35c16a64d2..d5fd7aa3e5 100644 --- a/pkg/lib/authenticationflow/declarative/generate_config_signup_login_flow_test.go +++ b/pkg/lib/authenticationflow/declarative/generate_config_signup_login_flow_test.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestGenerateSignupLoginFlowConfig(t *testing.T) { diff --git a/pkg/lib/config/config_test.go b/pkg/lib/config/config_test.go index c2ad27ffa0..9207a7dbf3 100644 --- a/pkg/lib/config/config_test.go +++ b/pkg/lib/config/config_test.go @@ -11,6 +11,14 @@ import ( "sigs.k8s.io/yaml" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) func TestAppConfig(t *testing.T) { diff --git a/pkg/lib/config/configsource/resources_test.go b/pkg/lib/config/configsource/resources_test.go index 0b6086a414..8910cb9ced 100644 --- a/pkg/lib/config/configsource/resources_test.go +++ b/pkg/lib/config/configsource/resources_test.go @@ -10,6 +10,8 @@ import ( apimodel "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" configtest "github.com/authgear/authgear-server/pkg/lib/config/test" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" "github.com/authgear/authgear-server/pkg/util/resource" ) diff --git a/pkg/lib/config/secret_test.go b/pkg/lib/config/secret_test.go index 0f77059635..bc12beed7d 100644 --- a/pkg/lib/config/secret_test.go +++ b/pkg/lib/config/secret_test.go @@ -10,6 +10,7 @@ import ( goyaml "gopkg.in/yaml.v2" "github.com/authgear/authgear-server/pkg/lib/config" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" ) func TestParseSecret(t *testing.T) { diff --git a/pkg/lib/tutorial/service_test.go b/pkg/lib/tutorial/service_test.go index 9a9030ca29..83ec8164a4 100644 --- a/pkg/lib/tutorial/service_test.go +++ b/pkg/lib/tutorial/service_test.go @@ -5,6 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" "github.com/authgear/authgear-server/pkg/util/resource" ) From d4d45d337cc1774e626ba052048bc7e57e036ad0 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 18:57:02 +0800 Subject: [PATCH 23/30] Workaround import cycle --- pkg/lib/authn/identity/candidate.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/lib/authn/identity/candidate.go b/pkg/lib/authn/identity/candidate.go index bd5910f16a..32bde0828e 100644 --- a/pkg/lib/authn/identity/candidate.go +++ b/pkg/lib/authn/identity/candidate.go @@ -4,7 +4,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) type Candidate map[string]interface{} @@ -28,13 +27,16 @@ const ( ) func NewOAuthCandidate(cfg oauthrelyingparty.ProviderConfig) Candidate { + // Ideally, we should import oauthrelyingparty/wechat and use ProviderConfig there. + // But that will result in import cycle. + app_type, _ := cfg["app_type"].(string) return Candidate{ CandidateKeyIdentityID: "", CandidateKeyType: string(model.IdentityTypeOAuth), CandidateKeyProviderType: string(cfg.Type()), CandidateKeyProviderAlias: cfg.Alias(), CandidateKeyProviderSubjectID: "", - CandidateKeyProviderAppType: string(wechat.ProviderConfig(cfg).AppType()), + CandidateKeyProviderAppType: app_type, CandidateKeyDisplayID: "", CandidateKeyModifyDisabled: cfg.ModifyDisabled(), } From 4f1d67e429ce0c0437cf68031f60f0590f13a327 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Fri, 10 May 2024 19:03:41 +0800 Subject: [PATCH 24/30] Hide Scope() in interface --- pkg/api/oauthrelyingparty/provider.go | 6 ------ pkg/lib/oauthrelyingparty/adfs/provider.go | 4 ++-- pkg/lib/oauthrelyingparty/apple/provider.go | 6 +++--- pkg/lib/oauthrelyingparty/azureadb2c/provider.go | 4 ++-- pkg/lib/oauthrelyingparty/azureadv2/provider.go | 4 ++-- pkg/lib/oauthrelyingparty/facebook/provider.go | 6 +++--- pkg/lib/oauthrelyingparty/github/provider.go | 6 +++--- pkg/lib/oauthrelyingparty/google/provider.go | 4 ++-- pkg/lib/oauthrelyingparty/linkedin/provider.go | 6 +++--- pkg/lib/oauthrelyingparty/wechat/provider.go | 6 +++--- 10 files changed, 23 insertions(+), 29 deletions(-) diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go index 5fc74e2e71..1427df5897 100644 --- a/pkg/api/oauthrelyingparty/provider.go +++ b/pkg/api/oauthrelyingparty/provider.go @@ -81,11 +81,6 @@ func (c ProviderConfig) SetDefaults() { provider.SetDefaults(c) } -func (c ProviderConfig) Scope() []string { - provider := c.MustGetProvider() - return provider.Scope(c) -} - func (c ProviderConfig) ProviderID() ProviderID { provider := c.MustGetProvider() return provider.ProviderID(c) @@ -175,7 +170,6 @@ type Dependencies struct { type Provider interface { SetDefaults(cfg ProviderConfig) ProviderID(cfg ProviderConfig) ProviderID - Scope(cfg ProviderConfig) []string GetAuthorizationURL(deps Dependencies, options GetAuthorizationURLOptions) (url string, err error) GetUserProfile(deps Dependencies, options GetUserProfileOptions) (UserProfile, error) } diff --git a/pkg/lib/oauthrelyingparty/adfs/provider.go b/pkg/lib/oauthrelyingparty/adfs/provider.go index a6abdb1776..f72ee89fae 100644 --- a/pkg/lib/oauthrelyingparty/adfs/provider.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider.go @@ -71,7 +71,7 @@ func (ADFS) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty.P return oauthrelyingparty.NewProviderID(cfg.Type(), nil) } -func (ADFS) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (ADFS) scope() []string { // The supported scopes are observed from a AD FS server. return []string{"openid", "profile", "email"} } @@ -89,7 +89,7 @@ func (p ADFS) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oau return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, diff --git a/pkg/lib/oauthrelyingparty/apple/provider.go b/pkg/lib/oauthrelyingparty/apple/provider.go index daa3554ea8..92af384487 100644 --- a/pkg/lib/oauthrelyingparty/apple/provider.go +++ b/pkg/lib/oauthrelyingparty/apple/provider.go @@ -102,7 +102,7 @@ func (Apple) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty. return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (Apple) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Apple) scope() []string { // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms return []string{"name", "email"} } @@ -141,11 +141,11 @@ func (Apple) createClientSecret(deps oauthrelyingparty.Dependencies) (clientSecr return } -func (Apple) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (p Apple) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return appleOIDCConfig.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go index 3f76e3dddb..1543f31e3c 100644 --- a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go @@ -87,7 +87,7 @@ func (AzureADB2C) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingp return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (AzureADB2C) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (AzureADB2C) scope() []string { // Instead of specifying scope to request a specific claim, // the developer must customize the policy to allow which claims are returned to the relying party. // If the developer is using User Flow policy, then those claims are called Application Claims. @@ -117,7 +117,7 @@ func (p AzureADB2C) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, par return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider.go b/pkg/lib/oauthrelyingparty/azureadv2/provider.go index 8d137f55de..d022474d0b 100644 --- a/pkg/lib/oauthrelyingparty/azureadv2/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider.go @@ -84,7 +84,7 @@ func (AzureADv2) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingpa return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (AzureADv2) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (AzureADv2) scope() []string { // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes return []string{"openid", "profile", "email"} } @@ -145,7 +145,7 @@ func (p AzureADv2) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, para return c.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, diff --git a/pkg/lib/oauthrelyingparty/facebook/provider.go b/pkg/lib/oauthrelyingparty/facebook/provider.go index 0d5de10c3b..0d5b26d11e 100644 --- a/pkg/lib/oauthrelyingparty/facebook/provider.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider.go @@ -79,16 +79,16 @@ func (Facebook) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingpar return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (Facebook) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Facebook) scope() []string { // https://developers.facebook.com/docs/permissions/reference return []string{"email", "public_profile"} } -func (Facebook) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (p Facebook) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(facebookAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset State: param.State, diff --git a/pkg/lib/oauthrelyingparty/github/provider.go b/pkg/lib/oauthrelyingparty/github/provider.go index 7d2c72275b..6cc4fdde8d 100644 --- a/pkg/lib/oauthrelyingparty/github/provider.go +++ b/pkg/lib/oauthrelyingparty/github/provider.go @@ -77,17 +77,17 @@ func (Github) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty return oauthrelyingparty.NewProviderID(cfg.Type(), nil) } -func (Github) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Github) scope() []string { // https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps return []string{"read:user", "user:email"} } -func (Github) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (p Github) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { // https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity return oauthrelyingpartyutil.MakeAuthorizationURL(githubAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), // ResponseType is unset. // ResponseMode is unset. State: param.State, diff --git a/pkg/lib/oauthrelyingparty/google/provider.go b/pkg/lib/oauthrelyingparty/google/provider.go index 8474ce9bbe..b954964117 100644 --- a/pkg/lib/oauthrelyingparty/google/provider.go +++ b/pkg/lib/oauthrelyingparty/google/provider.go @@ -71,7 +71,7 @@ func (Google) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty return oauthrelyingparty.NewProviderID(cfg.Type(), nil) } -func (Google) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Google) scope() []string { // https://developers.google.com/identity/protocols/oauth2/openid-connect return []string{"openid", "profile", "email"} } @@ -84,7 +84,7 @@ func (p Google) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param o return d.MakeOAuthURL(oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, ResponseMode: param.ResponseMode, State: param.State, diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider.go b/pkg/lib/oauthrelyingparty/linkedin/provider.go index dc989be162..fa9973ded2 100644 --- a/pkg/lib/oauthrelyingparty/linkedin/provider.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider.go @@ -76,17 +76,17 @@ func (Linkedin) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingpar return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (Linkedin) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Linkedin) scope() []string { // https://docs.microsoft.com/en-us/linkedin/shared/references/v2/profile/lite-profile // https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/primary-contact-api?context=linkedin/compliance/context return []string{"r_liteprofile", "r_emailaddress"} } -func (Linkedin) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (p Linkedin) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(linkedinAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ ClientID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, diff --git a/pkg/lib/oauthrelyingparty/wechat/provider.go b/pkg/lib/oauthrelyingparty/wechat/provider.go index 1445cd0874..01888d8f47 100644 --- a/pkg/lib/oauthrelyingparty/wechat/provider.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider.go @@ -127,17 +127,17 @@ func (Wechat) ProviderID(cfg oauthrelyingparty.ProviderConfig) oauthrelyingparty return oauthrelyingparty.NewProviderID(cfg.Type(), keys) } -func (Wechat) Scope(_ oauthrelyingparty.ProviderConfig) []string { +func (Wechat) scope() []string { // https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html return []string{"snsapi_userinfo"} } -func (Wechat) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { +func (p Wechat) GetAuthorizationURL(deps oauthrelyingparty.Dependencies, param oauthrelyingparty.GetAuthorizationURLOptions) (string, error) { return oauthrelyingpartyutil.MakeAuthorizationURL(wechatAuthorizationURL, oauthrelyingpartyutil.AuthorizationURLParams{ // ClientID is not used by wechat. WechatAppID: deps.ProviderConfig.ClientID(), RedirectURI: param.RedirectURI, - Scope: deps.ProviderConfig.Scope(), + Scope: p.scope(), ResponseType: oauthrelyingparty.ResponseTypeCode, // ResponseMode is unset. State: param.State, From 63345a4790ea956d1e492be019122fc8a63f118b Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Mon, 13 May 2024 13:59:59 +0800 Subject: [PATCH 25/30] Switch to depend on github.com/authgear/oauthrelyingparty --- go.mod | 5 +- go.sum | 2 + pkg/api/oauthrelyingparty/error.go | 7 - pkg/api/oauthrelyingparty/provider.go | 175 ------------------ pkg/api/oauthrelyingparty/registry.go | 15 -- pkg/api/oauthrelyingparty/response_mode.go | 6 - pkg/api/oauthrelyingparty/response_type.go | 5 - pkg/auth/handler/webapp/authflow_login.go | 2 +- pkg/auth/handler/webapp/authflow_promote.go | 2 +- pkg/auth/handler/webapp/authflow_signup.go | 2 +- .../authflowv2/internal_signup_login.go | 2 +- pkg/auth/handler/webapp/authflowv2/login.go | 2 +- pkg/auth/handler/webapp/authflowv2/promote.go | 2 +- pkg/auth/handler/webapp/identity_test.go | 2 +- .../declarative/input_step_identify.go | 2 +- .../input_take_oauth_authorization_request.go | 2 +- .../declarative/utils_common.go | 2 +- pkg/lib/authenticationflow/dependencies.go | 2 +- pkg/lib/authn/identity/candidate.go | 2 +- pkg/lib/authn/identity/info.go | 2 +- pkg/lib/authn/identity/info_test.go | 2 +- pkg/lib/authn/identity/oauth/provider.go | 5 +- pkg/lib/authn/identity/oauth/store.go | 2 +- pkg/lib/authn/identity/oauth_identity.go | 4 +- pkg/lib/authn/identity/oauth_spec.go | 2 +- pkg/lib/authn/identity/service/service.go | 5 +- .../identity/service/service_mock_test.go | 2 +- .../authn/identity/service/service_test.go | 2 +- pkg/lib/authn/sso/oauth_provider.go | 2 +- pkg/lib/config/feature_identity.go | 2 +- pkg/lib/config/identity.go | 2 +- pkg/lib/facade/coordinator.go | 5 +- pkg/lib/interaction/context.go | 2 +- .../nodes/use_identity_oauth_provider.go | 2 +- .../nodes/use_identity_oauth_user_info.go | 2 +- pkg/lib/oauthrelyingparty/adfs/provider.go | 2 +- .../oauthrelyingparty/adfs/provider_test.go | 2 +- pkg/lib/oauthrelyingparty/apple/provider.go | 2 +- .../oauthrelyingparty/apple/provider_test.go | 2 +- .../oauthrelyingparty/azureadb2c/provider.go | 2 +- .../azureadb2c/provider_test.go | 2 +- .../oauthrelyingparty/azureadv2/provider.go | 2 +- .../azureadv2/provider_test.go | 2 +- pkg/lib/oauthrelyingparty/builtin.go | 2 +- .../oauthrelyingparty/facebook/provider.go | 2 +- .../facebook/provider_test.go | 2 +- pkg/lib/oauthrelyingparty/github/provider.go | 2 +- .../oauthrelyingparty/github/provider_test.go | 2 +- pkg/lib/oauthrelyingparty/google/provider.go | 2 +- .../oauthrelyingparty/google/provider_test.go | 2 +- .../oauthrelyingparty/linkedin/provider.go | 2 +- .../linkedin/provider_test.go | 2 +- .../oauthrelyingpartyutil/access_token.go | 2 +- .../oauthrelyingpartyutil/claim_config.go | 2 +- .../oauthrelyingpartyutil/error.go | 2 +- .../oauthrelyingpartyutil/oidc.go | 2 +- pkg/lib/oauthrelyingparty/wechat/provider.go | 2 +- .../oauthrelyingparty/wechat/provider_test.go | 2 +- 58 files changed, 64 insertions(+), 264 deletions(-) delete mode 100644 pkg/api/oauthrelyingparty/error.go delete mode 100644 pkg/api/oauthrelyingparty/provider.go delete mode 100644 pkg/api/oauthrelyingparty/registry.go delete mode 100644 pkg/api/oauthrelyingparty/response_mode.go delete mode 100644 pkg/api/oauthrelyingparty/response_type.go diff --git a/go.mod b/go.mod index 478a03c6a2..d2ec3c4ec2 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,10 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require github.com/go-jose/go-jose/v3 v3.0.3 +require ( + github.com/authgear/oauthrelyingparty v1.0.0 + github.com/go-jose/go-jose/v3 v3.0.3 +) require ( cloud.google.com/go v0.110.8 // indirect diff --git a/go.sum b/go.sum index 15ab7e02a3..0cb1eb0577 100644 --- a/go.sum +++ b/go.sum @@ -87,6 +87,8 @@ github.com/alicebob/miniredis/v2 v2.31.0 h1:ObEFUNlJwoIiyjxdrYF0QIDE7qXcLc7D3WpS github.com/alicebob/miniredis/v2 v2.31.0/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg= github.com/authgear/graphql-go-relay v0.0.0-20201016065100-df672205b892 h1:OIPk6DEk51wL35vsCYfLacc7pZ0ev8TKvmcp+EKOnPU= github.com/authgear/graphql-go-relay v0.0.0-20201016065100-df672205b892/go.mod h1:SxJGRjo7+1SFr8pfMghiCM17WWFSswRwMvtv6t1aDO8= +github.com/authgear/oauthrelyingparty v1.0.0 h1:i5F3mjnImWesbdOc7ZcjPcT5918FAwruduRmFTBMtdY= +github.com/authgear/oauthrelyingparty v1.0.0/go.mod h1:0UcO0p5eS9BPLulX6K7TvkXkuPeTn3QhAJ/UQg4fmiQ= github.com/aws/aws-sdk-go v1.47.9 h1:rarTsos0mA16q+huicGx0e560aYRtOucV5z2Mw23JRY= github.com/aws/aws-sdk-go v1.47.9/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= diff --git a/pkg/api/oauthrelyingparty/error.go b/pkg/api/oauthrelyingparty/error.go deleted file mode 100644 index 4007e33470..0000000000 --- a/pkg/api/oauthrelyingparty/error.go +++ /dev/null @@ -1,7 +0,0 @@ -package oauthrelyingparty - -type ErrorResponse struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description,omitempty"` - ErrorURI string `json:"error_uri,omitempty"` -} diff --git a/pkg/api/oauthrelyingparty/provider.go b/pkg/api/oauthrelyingparty/provider.go deleted file mode 100644 index 1427df5897..0000000000 --- a/pkg/api/oauthrelyingparty/provider.go +++ /dev/null @@ -1,175 +0,0 @@ -package oauthrelyingparty - -import ( - "fmt" - "net/http" - "time" -) - -type ProviderConfig map[string]interface{} - -func (c ProviderConfig) MustGetProvider() Provider { - typ := c.Type() - p, ok := registry[typ] - if !ok { - panic(fmt.Errorf("oauth provider not in registry: %v", typ)) - } - return p -} - -func (c ProviderConfig) Alias() string { - alias, _ := c["alias"].(string) - return alias -} - -func (c ProviderConfig) Type() string { - typ, _ := c["type"].(string) - return typ -} - -func (c ProviderConfig) ClientID() string { - client_id, _ := c["client_id"].(string) - return client_id -} - -func (c ProviderConfig) SetDefaultsModifyDisabledFalse() { - _, ok := c["modify_disabled"].(bool) - if !ok { - c["modify_disabled"] = false - } -} - -func (c ProviderConfig) ModifyDisabled() bool { - modify_disabled, _ := c["modify_disabled"].(bool) - return modify_disabled -} - -func (c ProviderConfig) SetDefaultsEmailClaimConfig(claim ProviderClaimConfig) { - claims, ok := c["claims"].(map[string]interface{}) - if !ok { - claims = map[string]interface{}{} - c["claims"] = claims - } - - email, ok := claims["email"].(map[string]interface{}) - if !ok { - claims["email"] = map[string]interface{}(claim) - } else { - if _, ok := email["assume_verified"].(bool); !ok { - email["assume_verified"] = claim.AssumeVerified() - } - if _, ok := email["required"].(bool); !ok { - email["required"] = claim.Required() - } - } -} - -func (c ProviderConfig) EmailClaimConfig() ProviderClaimConfig { - claims, ok := c["claims"].(map[string]interface{}) - if !ok { - return ProviderClaimConfig{} - } - email, ok := claims["email"].(map[string]interface{}) - if !ok { - return ProviderClaimConfig{} - } - return ProviderClaimConfig(email) -} - -func (c ProviderConfig) SetDefaults() { - provider := c.MustGetProvider() - provider.SetDefaults(c) -} - -func (c ProviderConfig) ProviderID() ProviderID { - provider := c.MustGetProvider() - return provider.ProviderID(c) -} - -type ProviderClaimConfig map[string]interface{} - -func (c ProviderClaimConfig) AssumeVerified() bool { - b, _ := c["assume_verified"].(bool) - return b -} - -func (c ProviderClaimConfig) Required() bool { - b, _ := c["required"].(bool) - return b -} - -// ProviderID combining with a subject ID identifies an user from an external system. -type ProviderID struct { - Type string - Keys map[string]interface{} -} - -func NewProviderID(typ string, keys map[string]interface{}) ProviderID { - id := ProviderID{ - Keys: make(map[string]interface{}), - } - id.Type = typ - for k, v := range keys { - id.Keys[k] = v - } - return id -} - -func (i ProviderID) Equal(that ProviderID) bool { - if i.Type != that.Type { - return false - } - if len(i.Keys) != len(that.Keys) { - return false - } - for key, thisValue := range i.Keys { - thatValue, ok := that.Keys[key] - if !ok { - return false - } - if thisValue != thatValue { - return false - } - } - return true -} - -type GetAuthorizationURLOptions struct { - RedirectURI string - ResponseMode string - Nonce string - State string - Prompt []string -} - -type GetUserProfileOptions struct { - Code string - RedirectURI string - Nonce string -} - -type UserProfile struct { - ProviderRawProfile map[string]interface{} - // ProviderUserID is not necessarily equal to sub. - // If there exists a more unique identifier than sub, that identifier is chosen instead. - ProviderUserID string - StandardAttributes map[string]interface{} -} - -type Clock interface { - NowUTC() time.Time -} - -type Dependencies struct { - Clock Clock - ProviderConfig ProviderConfig - ClientSecret string - HTTPClient *http.Client -} - -type Provider interface { - SetDefaults(cfg ProviderConfig) - ProviderID(cfg ProviderConfig) ProviderID - GetAuthorizationURL(deps Dependencies, options GetAuthorizationURLOptions) (url string, err error) - GetUserProfile(deps Dependencies, options GetUserProfileOptions) (UserProfile, error) -} diff --git a/pkg/api/oauthrelyingparty/registry.go b/pkg/api/oauthrelyingparty/registry.go deleted file mode 100644 index 6996b56c2f..0000000000 --- a/pkg/api/oauthrelyingparty/registry.go +++ /dev/null @@ -1,15 +0,0 @@ -package oauthrelyingparty - -import ( - "fmt" -) - -var registry = map[string]Provider{} - -func RegisterProvider(typ string, provider Provider) { - _, ok := registry[typ] - if ok { - panic(fmt.Errorf("oauth provider is already registered: %v", typ)) - } - registry[typ] = provider -} diff --git a/pkg/api/oauthrelyingparty/response_mode.go b/pkg/api/oauthrelyingparty/response_mode.go deleted file mode 100644 index 8901b3f129..0000000000 --- a/pkg/api/oauthrelyingparty/response_mode.go +++ /dev/null @@ -1,6 +0,0 @@ -package oauthrelyingparty - -const ( - ResponseModeFormPost = "form_post" - ResponseModeQuery = "query" -) diff --git a/pkg/api/oauthrelyingparty/response_type.go b/pkg/api/oauthrelyingparty/response_type.go deleted file mode 100644 index a55fad1101..0000000000 --- a/pkg/api/oauthrelyingparty/response_type.go +++ /dev/null @@ -1,5 +0,0 @@ -package oauthrelyingparty - -const ( - ResponseTypeCode = "code" -) diff --git a/pkg/auth/handler/webapp/authflow_login.go b/pkg/auth/handler/webapp/authflow_login.go index ac7217a604..fdfcf4c817 100644 --- a/pkg/auth/handler/webapp/authflow_login.go +++ b/pkg/auth/handler/webapp/authflow_login.go @@ -6,7 +6,6 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" @@ -15,6 +14,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowLoginHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflow_promote.go b/pkg/auth/handler/webapp/authflow_promote.go index 79d2cc068c..71fd53f7fe 100644 --- a/pkg/auth/handler/webapp/authflow_promote.go +++ b/pkg/auth/handler/webapp/authflow_promote.go @@ -4,13 +4,13 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowPromoteHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflow_signup.go b/pkg/auth/handler/webapp/authflow_signup.go index 73c206c70c..6828055055 100644 --- a/pkg/auth/handler/webapp/authflow_signup.go +++ b/pkg/auth/handler/webapp/authflow_signup.go @@ -5,7 +5,6 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" @@ -14,6 +13,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowSignupHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go index 416adbd5ea..1903ed9267 100644 --- a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go +++ b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" @@ -15,6 +14,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowV2SignupHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/login.go b/pkg/auth/handler/webapp/authflowv2/login.go index 3a33e9e466..3de555bbea 100644 --- a/pkg/auth/handler/webapp/authflowv2/login.go +++ b/pkg/auth/handler/webapp/authflowv2/login.go @@ -6,7 +6,6 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" @@ -17,6 +16,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowLoginHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/promote.go b/pkg/auth/handler/webapp/authflowv2/promote.go index 2651eac964..691d2852bb 100644 --- a/pkg/auth/handler/webapp/authflowv2/promote.go +++ b/pkg/auth/handler/webapp/authflowv2/promote.go @@ -4,13 +4,13 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var AuthflowPromoteLoginIDSchema = validation.NewSimpleSchema(` diff --git a/pkg/auth/handler/webapp/identity_test.go b/pkg/auth/handler/webapp/identity_test.go index 61dda06131..b2a6f666bd 100644 --- a/pkg/auth/handler/webapp/identity_test.go +++ b/pkg/auth/handler/webapp/identity_test.go @@ -6,9 +6,9 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/lib/authn/identity" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestIdentitiesDisplayName(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/input_step_identify.go b/pkg/lib/authenticationflow/declarative/input_step_identify.go index 0e22bc9920..370854e43d 100644 --- a/pkg/lib/authenticationflow/declarative/input_step_identify.go +++ b/pkg/lib/authenticationflow/declarative/input_step_identify.go @@ -5,10 +5,10 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type InputSchemaStepIdentify struct { diff --git a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go index c7bd4e9507..3af96120db 100644 --- a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go +++ b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go @@ -5,10 +5,10 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type InputSchemaTakeOAuthAuthorizationRequest struct { diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index cf6407db62..19a148456d 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -8,7 +8,6 @@ import ( "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/identity" @@ -21,6 +20,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/phone" "github.com/authgear/authgear-server/pkg/util/uuid" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) diff --git a/pkg/lib/authenticationflow/dependencies.go b/pkg/lib/authenticationflow/dependencies.go index c028388315..faf68ac349 100644 --- a/pkg/lib/authenticationflow/dependencies.go +++ b/pkg/lib/authenticationflow/dependencies.go @@ -9,7 +9,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/accountmigration" "github.com/authgear/authgear-server/pkg/lib/authn/attrs" "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" @@ -32,6 +31,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/accesscontrol" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/httputil" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type IdentityService interface { diff --git a/pkg/lib/authn/identity/candidate.go b/pkg/lib/authn/identity/candidate.go index 32bde0828e..78a16b9bc2 100644 --- a/pkg/lib/authn/identity/candidate.go +++ b/pkg/lib/authn/identity/candidate.go @@ -2,8 +2,8 @@ package identity import ( "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Candidate map[string]interface{} diff --git a/pkg/lib/authn/identity/info.go b/pkg/lib/authn/identity/info.go index 5ab2a3374c..d8e1b62c27 100644 --- a/pkg/lib/authn/identity/info.go +++ b/pkg/lib/authn/identity/info.go @@ -5,8 +5,8 @@ import ( "time" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Info struct { diff --git a/pkg/lib/authn/identity/info_test.go b/pkg/lib/authn/identity/info_test.go index bd98ecdb11..a016c1e702 100644 --- a/pkg/lib/authn/identity/info_test.go +++ b/pkg/lib/authn/identity/info_test.go @@ -8,7 +8,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestInfoJSON(t *testing.T) { diff --git a/pkg/lib/authn/identity/oauth/provider.go b/pkg/lib/authn/identity/oauth/provider.go index d21c1ef180..0717d26a4d 100644 --- a/pkg/lib/authn/identity/oauth/provider.go +++ b/pkg/lib/authn/identity/oauth/provider.go @@ -3,12 +3,13 @@ package oauth import ( "sort" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/uuid" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) type Provider struct { diff --git a/pkg/lib/authn/identity/oauth/store.go b/pkg/lib/authn/identity/oauth/store.go index b3021d081e..75f7ca8569 100644 --- a/pkg/lib/authn/identity/oauth/store.go +++ b/pkg/lib/authn/identity/oauth/store.go @@ -10,11 +10,11 @@ import ( "github.com/lib/pq" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/lib/infra/db/appdb" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Store struct { diff --git a/pkg/lib/authn/identity/oauth_identity.go b/pkg/lib/authn/identity/oauth_identity.go index 3b859712a0..e3984c7ceb 100644 --- a/pkg/lib/authn/identity/oauth_identity.go +++ b/pkg/lib/authn/identity/oauth_identity.go @@ -3,9 +3,9 @@ package identity import ( "time" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/mail" "github.com/authgear/authgear-server/pkg/util/phone" ) diff --git a/pkg/lib/authn/identity/oauth_spec.go b/pkg/lib/authn/identity/oauth_spec.go index 009ad266cc..1e0ff14341 100644 --- a/pkg/lib/authn/identity/oauth_spec.go +++ b/pkg/lib/authn/identity/oauth_spec.go @@ -1,7 +1,7 @@ package identity import ( - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type OAuthSpec struct { diff --git a/pkg/lib/authn/identity/service/service.go b/pkg/lib/authn/identity/service/service.go index 4d547f8f5e..6adedbbbb1 100644 --- a/pkg/lib/authn/identity/service/service.go +++ b/pkg/lib/authn/identity/service/service.go @@ -4,12 +4,13 @@ import ( "errors" "fmt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) //go:generate mockgen -source=service.go -destination=service_mock_test.go -package service diff --git a/pkg/lib/authn/identity/service/service_mock_test.go b/pkg/lib/authn/identity/service/service_mock_test.go index 5da7e1271d..cc431786e8 100644 --- a/pkg/lib/authn/identity/service/service_mock_test.go +++ b/pkg/lib/authn/identity/service/service_mock_test.go @@ -7,9 +7,9 @@ package service import ( reflect "reflect" - oauthrelyingparty "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" identity "github.com/authgear/authgear-server/pkg/lib/authn/identity" loginid "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" + oauthrelyingparty "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" gomock "github.com/golang/mock/gomock" jsonpointer "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) diff --git a/pkg/lib/authn/identity/service/service_test.go b/pkg/lib/authn/identity/service/service_test.go index 876d2a444c..9208742959 100644 --- a/pkg/lib/authn/identity/service/service_test.go +++ b/pkg/lib/authn/identity/service/service_test.go @@ -8,10 +8,10 @@ import ( . "github.com/smartystreets/goconvey/convey" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func newBool(b bool) *bool { diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 405d08f37d..1c131ef5c6 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -2,10 +2,10 @@ package sso import ( "github.com/authgear/authgear-server/pkg/api" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type StandardAttributesNormalizer interface { diff --git a/pkg/lib/config/feature_identity.go b/pkg/lib/config/feature_identity.go index 3a80bb438b..4d0b69823b 100644 --- a/pkg/lib/config/feature_identity.go +++ b/pkg/lib/config/feature_identity.go @@ -1,8 +1,8 @@ package config import ( - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var _ = FeatureConfigSchema.Add("IdentityFeatureConfig", ` diff --git a/pkg/lib/config/identity.go b/pkg/lib/config/identity.go index f9bbb35efd..b83ac15aa2 100644 --- a/pkg/lib/config/identity.go +++ b/pkg/lib/config/identity.go @@ -2,7 +2,7 @@ package config import ( "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var _ = Schema.Add("IdentityConfig", ` diff --git a/pkg/lib/facade/coordinator.go b/pkg/lib/facade/coordinator.go index 4930baa321..7a382d6bb6 100644 --- a/pkg/lib/facade/coordinator.go +++ b/pkg/lib/facade/coordinator.go @@ -3,13 +3,15 @@ package facade import ( "errors" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/event/blocking" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator/service" @@ -22,7 +24,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/accesscontrol" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/errorutil" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) type EventService interface { diff --git a/pkg/lib/interaction/context.go b/pkg/lib/interaction/context.go index 476fcb9c69..2e3a41526d 100644 --- a/pkg/lib/interaction/context.go +++ b/pkg/lib/interaction/context.go @@ -7,7 +7,6 @@ import ( "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator/service" @@ -31,6 +30,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/accesscontrol" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/httputil" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type IdentityService interface { diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 0d5f2ef7b6..80be423718 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -4,12 +4,12 @@ import ( "net/url" "github.com/authgear/authgear-server/pkg/api" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/crypto" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 02024068a1..60431f0ffe 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -5,11 +5,11 @@ import ( "fmt" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/adfs/provider.go b/pkg/lib/oauthrelyingparty/adfs/provider.go index f72ee89fae..654e0255ce 100644 --- a/pkg/lib/oauthrelyingparty/adfs/provider.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider.go @@ -3,11 +3,11 @@ package adfs import ( "context" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/adfs/provider_test.go b/pkg/lib/oauthrelyingparty/adfs/provider_test.go index b767fd3ab0..659a13e375 100644 --- a/pkg/lib/oauthrelyingparty/adfs/provider_test.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider_test.go @@ -7,7 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestADFS(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/apple/provider.go b/pkg/lib/oauthrelyingparty/apple/provider.go index 92af384487..81b8ff8eb2 100644 --- a/pkg/lib/oauthrelyingparty/apple/provider.go +++ b/pkg/lib/oauthrelyingparty/apple/provider.go @@ -8,7 +8,6 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" @@ -16,6 +15,7 @@ import ( "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwtutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/apple/provider_test.go b/pkg/lib/oauthrelyingparty/apple/provider_test.go index 0133c8bf87..7af04ac170 100644 --- a/pkg/lib/oauthrelyingparty/apple/provider_test.go +++ b/pkg/lib/oauthrelyingparty/apple/provider_test.go @@ -5,7 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestAppleImpl(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go index 1543f31e3c..7996192b5c 100644 --- a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go @@ -4,11 +4,11 @@ import ( "context" "fmt" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go index 5952bfe644..89009d77ea 100644 --- a/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider_test.go @@ -7,7 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestAzureadb2cImpl(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider.go b/pkg/lib/oauthrelyingparty/azureadv2/provider.go index d022474d0b..2dd12f762e 100644 --- a/pkg/lib/oauthrelyingparty/azureadv2/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider.go @@ -4,11 +4,11 @@ import ( "context" "fmt" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go b/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go index fc9f20f9d7..b1ad2dd297 100644 --- a/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider_test.go @@ -7,7 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestAzureadv2Impl(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/builtin.go b/pkg/lib/oauthrelyingparty/builtin.go index 8da1e3c7de..25e706ad76 100644 --- a/pkg/lib/oauthrelyingparty/builtin.go +++ b/pkg/lib/oauthrelyingparty/builtin.go @@ -1,8 +1,8 @@ package oauthrelyingparty import ( - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) const ( diff --git a/pkg/lib/oauthrelyingparty/facebook/provider.go b/pkg/lib/oauthrelyingparty/facebook/provider.go index 0d5b26d11e..ecbf5fe4e2 100644 --- a/pkg/lib/oauthrelyingparty/facebook/provider.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider.go @@ -3,12 +3,12 @@ package facebook import ( "net/url" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/facebook/provider_test.go b/pkg/lib/oauthrelyingparty/facebook/provider_test.go index b81fbcca0e..68cbfe8d64 100644 --- a/pkg/lib/oauthrelyingparty/facebook/provider_test.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider_test.go @@ -5,7 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestFacebook(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/github/provider.go b/pkg/lib/oauthrelyingparty/github/provider.go index 6cc4fdde8d..c9f535d90c 100644 --- a/pkg/lib/oauthrelyingparty/github/provider.go +++ b/pkg/lib/oauthrelyingparty/github/provider.go @@ -8,12 +8,12 @@ import ( "strings" "github.com/authgear/authgear-server/pkg/api/apierrors" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/github/provider_test.go b/pkg/lib/oauthrelyingparty/github/provider_test.go index e5861f14fa..974eb0e481 100644 --- a/pkg/lib/oauthrelyingparty/github/provider_test.go +++ b/pkg/lib/oauthrelyingparty/github/provider_test.go @@ -5,7 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestGithub(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/google/provider.go b/pkg/lib/oauthrelyingparty/google/provider.go index b954964117..014c8b1a2b 100644 --- a/pkg/lib/oauthrelyingparty/google/provider.go +++ b/pkg/lib/oauthrelyingparty/google/provider.go @@ -3,11 +3,11 @@ package google import ( "context" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/google/provider_test.go b/pkg/lib/oauthrelyingparty/google/provider_test.go index 3a9413f4dc..5a6d9f3b55 100644 --- a/pkg/lib/oauthrelyingparty/google/provider_test.go +++ b/pkg/lib/oauthrelyingparty/google/provider_test.go @@ -7,7 +7,7 @@ import ( . "github.com/smartystreets/goconvey/convey" "gopkg.in/h2non/gock.v1" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestGoogleImpl(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider.go b/pkg/lib/oauthrelyingparty/linkedin/provider.go index fa9973ded2..4417499bce 100644 --- a/pkg/lib/oauthrelyingparty/linkedin/provider.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider.go @@ -1,11 +1,11 @@ package linkedin import ( - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider_test.go b/pkg/lib/oauthrelyingparty/linkedin/provider_test.go index 75f6a62d69..4710d1a336 100644 --- a/pkg/lib/oauthrelyingparty/linkedin/provider_test.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider_test.go @@ -5,7 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestLinkedin(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go index 0c590274d3..678f7afd7f 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/access_token.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type AccessTokenResp map[string]interface{} diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go index b28c7b5c87..d811066859 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/claim_config.go @@ -1,6 +1,6 @@ package oauthrelyingpartyutil -import "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" +import "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" func Email_AssumeVerified_Required() oauthrelyingparty.ProviderClaimConfig { return oauthrelyingparty.ProviderClaimConfig{ diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go index 3d488ae6e3..b3ef718286 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go @@ -2,7 +2,7 @@ package oauthrelyingpartyutil import ( "github.com/authgear/authgear-server/pkg/api/apierrors" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var InvalidConfiguration = apierrors.InternalError.WithReason("InvalidConfiguration") diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go index 2b36e5b29c..daf2730332 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go @@ -11,9 +11,9 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwsutil" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type jwtClock struct { diff --git a/pkg/lib/oauthrelyingparty/wechat/provider.go b/pkg/lib/oauthrelyingparty/wechat/provider.go index 01888d8f47..30b63fa38b 100644 --- a/pkg/lib/oauthrelyingparty/wechat/provider.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider.go @@ -9,11 +9,11 @@ import ( "net/url" "strconv" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/wechat/provider_test.go b/pkg/lib/oauthrelyingparty/wechat/provider_test.go index 55818707c6..452e5281c4 100644 --- a/pkg/lib/oauthrelyingparty/wechat/provider_test.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider_test.go @@ -5,7 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/oauthrelyingparty" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestWechat(t *testing.T) { From 5d3494f2e791921859190b9ae5493ef7408f4e57 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Mon, 13 May 2024 14:13:04 +0800 Subject: [PATCH 26/30] Use goimports instead of go fmt goimports can sort imports. --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 553371d7bb..5745274b3c 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ vendor: go install github.com/golang/mock/mockgen go install github.com/google/wire/cmd/wire go install golang.org/x/vuln/cmd/govulncheck@latest + go install golang.org/x/tools/cmd/goimports@latest npm --prefix ./scripts/npm ci npm --prefix ./authui ci npm --prefix ./portal ci @@ -57,7 +58,8 @@ lint: .PHONY: fmt fmt: - go fmt ./... + # Ignore generated files, such as wire_gen.go and *_mock_test.go + find ./pkg ./cmd ./e2e -name '*.go' -not -name 'wire_gen.go' -not -name '*_mock_test.go' | sort | xargs goimports -w -format-only -local github.com/authgear/authgear-server .PHONY: govulncheck govulncheck: From d54d90f452d87f848e30ddb5cb461468c5ebba0a Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Mon, 13 May 2024 14:13:30 +0800 Subject: [PATCH 27/30] Run make fmt --- cmd/authgear/background/wire.go | 1 + cmd/portal/analytic/wire.go | 3 ++- cmd/portal/cmd/cmdanalytic/posthog.go | 1 + .../cmdinternal/migrate_resources_remove_is_first_party.go | 5 +++-- cmd/portal/server/wire.go | 1 + cmd/portal/usage/deps.go | 3 ++- cmd/portal/usage/wire.go | 3 ++- e2e/cmd/e2e/cmd/configsource.go | 3 ++- e2e/cmd/e2e/cmd/execsql.go | 3 ++- e2e/cmd/e2e/cmd/otp.go | 3 ++- e2e/cmd/e2e/cmd/userimport.go | 3 ++- e2e/cmd/e2e/main.go | 5 +++-- e2e/cmd/e2e/pkg/deps.go | 3 ++- e2e/cmd/proxy/main.go | 5 +++-- e2e/cmd/proxy/modifier/oidc.go | 3 ++- e2e/pkg/testrunner/testcase.go | 1 + pkg/admin/graphql/group_mutation.go | 3 ++- pkg/admin/graphql/group_role_mutation.go | 3 ++- pkg/admin/graphql/group_user_mutation.go | 5 +++-- pkg/admin/graphql/role_mutation.go | 3 ++- pkg/admin/graphql/role_user_mutation.go | 5 +++-- pkg/api/model/siwe.go | 3 ++- pkg/api/model/siwe_test.go | 3 ++- pkg/auth/handler/api/authenticationflow_v1_create.go | 3 ++- pkg/auth/handler/webapp/authflow_controller.go | 3 ++- pkg/auth/handler/webapp/authflow_login.go | 3 ++- pkg/auth/handler/webapp/authflow_promote.go | 3 ++- pkg/auth/handler/webapp/authflow_signup.go | 3 ++- pkg/auth/handler/webapp/authflowv2/internal_signup_login.go | 3 ++- pkg/auth/handler/webapp/authflowv2/login.go | 3 ++- pkg/auth/handler/webapp/authflowv2/promote.go | 3 ++- pkg/auth/handler/webapp/identity_test.go | 3 ++- pkg/auth/handler/webapp/user_profile_test.go | 3 ++- pkg/auth/handler/webapp/viewmodels/authflow_branch_test.go | 3 ++- pkg/auth/handler/webapp/wire.go | 3 ++- pkg/auth/webapp/dynamic_csp_middleware_test.go | 3 ++- pkg/lib/analytic/appdb_store.go | 1 + pkg/lib/analytic/chart_service_test.go | 3 ++- pkg/lib/analytic/time_test.go | 3 ++- pkg/lib/authenticationflow/allowlist_test.go | 3 ++- .../declarative/input_login_flow_step_authenticate_test.go | 3 ++- .../authenticationflow/declarative/input_step_identify.go | 3 ++- .../declarative/input_take_oauth_authorization_request.go | 3 ++- .../declarative/input_take_oob_otp_channel_test.go | 3 ++- .../authenticationflow/declarative/intent_account_linking.go | 3 ++- .../declarative/intent_check_conflict_and_create_identity.go | 3 ++- pkg/lib/authenticationflow/declarative/milestone.go | 3 ++- .../declarative/node_do_use_authenticator_password.go | 3 ++- .../declarative/synthetic_input_passkey.go | 3 ++- .../authenticationflow/declarative/utils_account_linking.go | 3 ++- pkg/lib/authenticationflow/declarative/utils_common.go | 5 +++-- pkg/lib/authenticationflow/dependencies.go | 3 ++- pkg/lib/authenticationflow/flow.go | 3 ++- pkg/lib/authenticationflow/service.go | 3 ++- pkg/lib/authn/identity/candidate.go | 3 ++- pkg/lib/authn/identity/info.go | 3 ++- pkg/lib/authn/identity/info_test.go | 3 ++- pkg/lib/authn/identity/loginid/provider.go | 3 ++- pkg/lib/authn/identity/oauth/store.go | 3 ++- pkg/lib/authn/identity/service/service_test.go | 3 ++- pkg/lib/authn/otp/code.go | 3 ++- pkg/lib/authn/otp/service.go | 3 ++- pkg/lib/authn/sso/deps.go | 3 ++- pkg/lib/authn/sso/oauth_provider.go | 3 ++- pkg/lib/cloudstorage/azure.go | 1 + pkg/lib/cloudstorage/gcs.go | 3 ++- pkg/lib/config/feature_identity.go | 3 ++- pkg/lib/config/feature_test.go | 3 ++- pkg/lib/config/identity.go | 3 ++- pkg/lib/config/rate_limits_env_test.go | 3 ++- pkg/lib/config/secret_data.go | 3 ++- pkg/lib/config/secret_update_instruction_context.go | 3 ++- pkg/lib/config/secret_update_instruction_test.go | 5 +++-- pkg/lib/db/util/dump.go | 3 ++- pkg/lib/facade/identity.go | 3 ++- pkg/lib/feature/forgotpassword/service.go | 3 ++- pkg/lib/feature/siwe/service.go | 3 ++- pkg/lib/feature/stdattrs/transformer_test.go | 3 ++- pkg/lib/images/model_test.go | 3 ++- pkg/lib/infra/whatsapp/service.go | 3 ++- pkg/lib/interaction/context.go | 3 ++- pkg/lib/interaction/nodes/use_identity_oauth_provider.go | 3 ++- pkg/lib/interaction/nodes/use_identity_oauth_user_info.go | 3 ++- pkg/lib/oauth/handler/handler_proxy_redirect_test.go | 3 ++- pkg/lib/oauthrelyingparty/adfs/provider.go | 3 ++- pkg/lib/oauthrelyingparty/apple/provider.go | 3 ++- pkg/lib/oauthrelyingparty/azureadb2c/provider.go | 3 ++- pkg/lib/oauthrelyingparty/azureadv2/provider.go | 3 ++- pkg/lib/oauthrelyingparty/builtin.go | 3 ++- pkg/lib/oauthrelyingparty/facebook/provider.go | 3 ++- pkg/lib/oauthrelyingparty/github/provider.go | 3 ++- pkg/lib/oauthrelyingparty/google/provider.go | 3 ++- pkg/lib/oauthrelyingparty/linkedin/provider.go | 3 ++- pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go | 3 ++- pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go | 3 ++- pkg/lib/oauthrelyingparty/wechat/provider.go | 3 ++- pkg/lib/rolesgroups/store_group_user.go | 3 ++- pkg/lib/rolesgroups/store_role_user.go | 3 ++- pkg/lib/rolesgroups/store_user.go | 3 ++- pkg/lib/sessionlisting/listing_test.go | 5 +++-- pkg/lib/web/embedded_resource_test.go | 3 ++- pkg/lib/workflow/event_store.go | 3 ++- pkg/portal/appresource/util.go | 3 ++- pkg/portal/graphql/billing.go | 3 ++- pkg/portal/graphql/billing_mutation.go | 5 +++-- pkg/portal/graphql/chart.go | 5 +++-- pkg/portal/graphql/context.go | 3 ++- pkg/portal/graphql/nodes.go | 3 ++- pkg/portal/service/kubernetes.go | 3 ++- pkg/portal/service/subscription.go | 1 + pkg/util/hexstring/hexstring_test.go | 3 ++- pkg/util/httpsigning/httpsigning_test.go | 3 ++- pkg/util/httputil/json_test.go | 3 ++- pkg/util/matchlist/matchlist_test.go | 3 ++- pkg/util/periodical/periodical_test.go | 3 ++- pkg/util/timeutil/isoweek_test.go | 3 ++- pkg/util/timeutil/truncate_test.go | 3 ++- pkg/util/web3/contractid_test.go | 3 ++- pkg/util/web3/eip55.go | 3 ++- pkg/util/web3/eip681_test.go | 3 ++- 120 files changed, 243 insertions(+), 123 deletions(-) diff --git a/cmd/authgear/background/wire.go b/cmd/authgear/background/wire.go index 718d612d4e..c45a324185 100644 --- a/cmd/authgear/background/wire.go +++ b/cmd/authgear/background/wire.go @@ -5,6 +5,7 @@ package background import ( "context" + "github.com/google/wire" "github.com/authgear/authgear-server/pkg/lib/config" diff --git a/cmd/portal/analytic/wire.go b/cmd/portal/analytic/wire.go index b140aa3083..66c9a6ff28 100644 --- a/cmd/portal/analytic/wire.go +++ b/cmd/portal/analytic/wire.go @@ -6,13 +6,14 @@ package analytic import ( "context" + "github.com/google/wire" + "github.com/authgear/authgear-server/pkg/lib/analytic" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/lib/infra/redis" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/periodical" - "github.com/google/wire" ) func NewUserWeeklyReport( diff --git a/cmd/portal/cmd/cmdanalytic/posthog.go b/cmd/portal/cmd/cmdanalytic/posthog.go index 8d05cf521f..19033c8736 100644 --- a/cmd/portal/cmd/cmdanalytic/posthog.go +++ b/cmd/portal/cmd/cmdanalytic/posthog.go @@ -2,6 +2,7 @@ package cmdanalytic import ( "context" + "github.com/spf13/cobra" "github.com/authgear/authgear-server/cmd/portal/analytic" diff --git a/cmd/portal/cmd/cmdinternal/migrate_resources_remove_is_first_party.go b/cmd/portal/cmd/cmdinternal/migrate_resources_remove_is_first_party.go index 48b2687862..9df7a6b61f 100644 --- a/cmd/portal/cmd/cmdinternal/migrate_resources_remove_is_first_party.go +++ b/cmd/portal/cmd/cmdinternal/migrate_resources_remove_is_first_party.go @@ -5,10 +5,11 @@ import ( "fmt" "log" - portalcmd "github.com/authgear/authgear-server/cmd/portal/cmd" - "github.com/authgear/authgear-server/cmd/portal/internal" "github.com/spf13/cobra" "sigs.k8s.io/yaml" + + portalcmd "github.com/authgear/authgear-server/cmd/portal/cmd" + "github.com/authgear/authgear-server/cmd/portal/internal" ) var cmdInternalMigrateRemoveIsFirstParty = &cobra.Command{ diff --git a/cmd/portal/server/wire.go b/cmd/portal/server/wire.go index dd6aa56774..7ac202d722 100644 --- a/cmd/portal/server/wire.go +++ b/cmd/portal/server/wire.go @@ -5,6 +5,7 @@ package server import ( "context" + "github.com/google/wire" "github.com/authgear/authgear-server/pkg/lib/config" diff --git a/cmd/portal/usage/deps.go b/cmd/portal/usage/deps.go index d6826fcb31..e985c9bfb4 100644 --- a/cmd/portal/usage/deps.go +++ b/cmd/portal/usage/deps.go @@ -1,6 +1,8 @@ package usage import ( + "github.com/google/wire" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db/auditdb" "github.com/authgear/authgear-server/pkg/lib/infra/db/globaldb" @@ -8,7 +10,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/meter" "github.com/authgear/authgear-server/pkg/lib/usage" "github.com/authgear/authgear-server/pkg/util/cobrasentry" - "github.com/google/wire" ) func NewGlobalDatabaseCredentials(dbCredentials *config.DatabaseCredentials) *config.GlobalDatabaseCredentialsEnvironmentConfig { diff --git a/cmd/portal/usage/wire.go b/cmd/portal/usage/wire.go index 49598fc712..74d32b797c 100644 --- a/cmd/portal/usage/wire.go +++ b/cmd/portal/usage/wire.go @@ -8,11 +8,12 @@ import ( "github.com/getsentry/sentry-go" + "github.com/google/wire" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/lib/infra/redis" "github.com/authgear/authgear-server/pkg/lib/usage" - "github.com/google/wire" ) func NewCountCollector( diff --git a/e2e/cmd/e2e/cmd/configsource.go b/e2e/cmd/e2e/cmd/configsource.go index d816e6906e..30ad995fe7 100644 --- a/e2e/cmd/e2e/cmd/configsource.go +++ b/e2e/cmd/e2e/cmd/configsource.go @@ -1,8 +1,9 @@ package cmd import ( - e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" "github.com/spf13/cobra" + + e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" ) func init() { diff --git a/e2e/cmd/e2e/cmd/execsql.go b/e2e/cmd/e2e/cmd/execsql.go index c0ad81f03b..0dfe3d4d59 100644 --- a/e2e/cmd/e2e/cmd/execsql.go +++ b/e2e/cmd/e2e/cmd/execsql.go @@ -1,8 +1,9 @@ package cmd import ( - e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" "github.com/spf13/cobra" + + e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" ) func init() { diff --git a/e2e/cmd/e2e/cmd/otp.go b/e2e/cmd/e2e/cmd/otp.go index 75bff289b7..c420b16856 100644 --- a/e2e/cmd/e2e/cmd/otp.go +++ b/e2e/cmd/e2e/cmd/otp.go @@ -3,8 +3,9 @@ package cmd import ( "fmt" - e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" "github.com/spf13/cobra" + + e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" ) func init() { diff --git a/e2e/cmd/e2e/cmd/userimport.go b/e2e/cmd/e2e/cmd/userimport.go index dc93884111..d106192ecb 100644 --- a/e2e/cmd/e2e/cmd/userimport.go +++ b/e2e/cmd/e2e/cmd/userimport.go @@ -3,8 +3,9 @@ package cmd import ( "os" - e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" "github.com/spf13/cobra" + + e2e "github.com/authgear/authgear-server/e2e/cmd/e2e/pkg" ) func init() { diff --git a/e2e/cmd/e2e/main.go b/e2e/cmd/e2e/main.go index 382f4bb64d..0a5f210f29 100644 --- a/e2e/cmd/e2e/main.go +++ b/e2e/cmd/e2e/main.go @@ -5,10 +5,11 @@ import ( "log" "os" - cmd "github.com/authgear/authgear-server/e2e/cmd/e2e/cmd" - "github.com/authgear/authgear-server/pkg/util/debug" "github.com/joho/godotenv" _ "go.uber.org/automaxprocs" + + cmd "github.com/authgear/authgear-server/e2e/cmd/e2e/cmd" + "github.com/authgear/authgear-server/pkg/util/debug" ) func main() { diff --git a/e2e/cmd/e2e/pkg/deps.go b/e2e/cmd/e2e/pkg/deps.go index 13a125319b..b9511498b3 100644 --- a/e2e/cmd/e2e/pkg/deps.go +++ b/e2e/cmd/e2e/pkg/deps.go @@ -3,9 +3,10 @@ package e2e import ( "net/http" + "github.com/google/wire" + deps "github.com/authgear/authgear-server/pkg/lib/deps" "github.com/authgear/authgear-server/pkg/util/httputil" - "github.com/google/wire" ) func ProvideEnd2EndHTTPRequest() *http.Request { diff --git a/e2e/cmd/proxy/main.go b/e2e/cmd/proxy/main.go index a74a18e209..ef184d7610 100644 --- a/e2e/cmd/proxy/main.go +++ b/e2e/cmd/proxy/main.go @@ -11,11 +11,12 @@ import ( "os/signal" "time" - "github.com/authgear/authgear-server/e2e/cmd/proxy/mockoidc" - "github.com/authgear/authgear-server/e2e/cmd/proxy/modifier" "github.com/google/martian" "github.com/google/martian/httpspec" "github.com/google/martian/mitm" + + "github.com/authgear/authgear-server/e2e/cmd/proxy/mockoidc" + "github.com/authgear/authgear-server/e2e/cmd/proxy/modifier" ) func main() { diff --git a/e2e/cmd/proxy/modifier/oidc.go b/e2e/cmd/proxy/modifier/oidc.go index b52bcd8c5e..3e1ab963a1 100644 --- a/e2e/cmd/proxy/modifier/oidc.go +++ b/e2e/cmd/proxy/modifier/oidc.go @@ -4,8 +4,9 @@ import ( "net/http" "net/url" - "github.com/authgear/authgear-server/e2e/cmd/proxy/mockoidc" "github.com/google/martian/parse" + + "github.com/authgear/authgear-server/e2e/cmd/proxy/mockoidc" ) func init() { diff --git a/e2e/pkg/testrunner/testcase.go b/e2e/pkg/testrunner/testcase.go index 057ac352db..f4adf98543 100644 --- a/e2e/pkg/testrunner/testcase.go +++ b/e2e/pkg/testrunner/testcase.go @@ -12,6 +12,7 @@ import ( texttemplate "text/template" "github.com/Masterminds/sprig" + authflowclient "github.com/authgear/authgear-server/e2e/pkg/e2eclient" "github.com/authgear/authgear-server/pkg/util/httputil" ) diff --git a/pkg/admin/graphql/group_mutation.go b/pkg/admin/graphql/group_mutation.go index 24fda28c4f..bbff0a09b9 100644 --- a/pkg/admin/graphql/group_mutation.go +++ b/pkg/admin/graphql/group_mutation.go @@ -3,13 +3,14 @@ package graphql import ( relay "github.com/authgear/graphql-go-relay" + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/rolesgroups" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/graphql-go/graphql" ) var createGroupInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/admin/graphql/group_role_mutation.go b/pkg/admin/graphql/group_role_mutation.go index 2805fc205a..c39a466184 100644 --- a/pkg/admin/graphql/group_role_mutation.go +++ b/pkg/admin/graphql/group_role_mutation.go @@ -1,12 +1,13 @@ package graphql import ( + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/rolesgroups" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/graphql-go/graphql" ) var addRoleToGroupsInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/admin/graphql/group_user_mutation.go b/pkg/admin/graphql/group_user_mutation.go index e5c64bd655..4cfe5714a3 100644 --- a/pkg/admin/graphql/group_user_mutation.go +++ b/pkg/admin/graphql/group_user_mutation.go @@ -1,14 +1,15 @@ package graphql import ( + relay "github.com/authgear/graphql-go-relay" + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/rolesgroups" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" - relay "github.com/authgear/graphql-go-relay" - "github.com/graphql-go/graphql" ) var addGroupToUsersInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/admin/graphql/role_mutation.go b/pkg/admin/graphql/role_mutation.go index 305be6ebea..4d45ec94b4 100644 --- a/pkg/admin/graphql/role_mutation.go +++ b/pkg/admin/graphql/role_mutation.go @@ -3,13 +3,14 @@ package graphql import ( relay "github.com/authgear/graphql-go-relay" + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/rolesgroups" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/graphql-go/graphql" ) var createRoleInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/admin/graphql/role_user_mutation.go b/pkg/admin/graphql/role_user_mutation.go index 8ef71d5f40..bf4808eec4 100644 --- a/pkg/admin/graphql/role_user_mutation.go +++ b/pkg/admin/graphql/role_user_mutation.go @@ -1,14 +1,15 @@ package graphql import ( + relay "github.com/authgear/graphql-go-relay" + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/rolesgroups" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" - relay "github.com/authgear/graphql-go-relay" - "github.com/graphql-go/graphql" ) var addRoleToUsersInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/api/model/siwe.go b/pkg/api/model/siwe.go index 178f6d6258..348be45bae 100644 --- a/pkg/api/model/siwe.go +++ b/pkg/api/model/siwe.go @@ -5,8 +5,9 @@ import ( "encoding/hex" "fmt" - "github.com/authgear/authgear-server/pkg/util/web3" "github.com/ethereum/go-ethereum/crypto" + + "github.com/authgear/authgear-server/pkg/util/web3" ) type SIWEPublicKey string diff --git a/pkg/api/model/siwe_test.go b/pkg/api/model/siwe_test.go index 2f4d69a9f0..e9d2816734 100644 --- a/pkg/api/model/siwe_test.go +++ b/pkg/api/model/siwe_test.go @@ -6,9 +6,10 @@ import ( "crypto/rand" "testing" - "github.com/authgear/authgear-server/pkg/api/model" . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/api/model" + "github.com/ethereum/go-ethereum/crypto" ) diff --git a/pkg/auth/handler/api/authenticationflow_v1_create.go b/pkg/auth/handler/api/authenticationflow_v1_create.go index 4dcf07da20..e75a0eb328 100644 --- a/pkg/auth/handler/api/authenticationflow_v1_create.go +++ b/pkg/auth/handler/api/authenticationflow_v1_create.go @@ -6,6 +6,8 @@ import ( "net/http" "net/url" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/infra/redis/appredis" @@ -17,7 +19,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/log" "github.com/authgear/authgear-server/pkg/util/slice" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func ConfigureAuthenticationFlowV1CreateRoute(route httproute.Route) httproute.Route { diff --git a/pkg/auth/handler/webapp/authflow_controller.go b/pkg/auth/handler/webapp/authflow_controller.go index 31066f311a..38ca187b8d 100644 --- a/pkg/auth/handler/webapp/authflow_controller.go +++ b/pkg/auth/handler/webapp/authflow_controller.go @@ -8,6 +8,8 @@ import ( "net/url" "strconv" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" @@ -24,7 +26,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/log" "github.com/authgear/authgear-server/pkg/util/setutil" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) //go:generate mockgen -source=authflow_controller.go -destination=authflow_controller_mock_test.go -package webapp diff --git a/pkg/auth/handler/webapp/authflow_login.go b/pkg/auth/handler/webapp/authflow_login.go index fdfcf4c817..b6523d4937 100644 --- a/pkg/auth/handler/webapp/authflow_login.go +++ b/pkg/auth/handler/webapp/authflow_login.go @@ -6,6 +6,8 @@ import ( "net/http" "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" @@ -14,7 +16,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowLoginHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflow_promote.go b/pkg/auth/handler/webapp/authflow_promote.go index 71fd53f7fe..64f2dcd16d 100644 --- a/pkg/auth/handler/webapp/authflow_promote.go +++ b/pkg/auth/handler/webapp/authflow_promote.go @@ -4,13 +4,14 @@ import ( "net/http" "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowPromoteHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflow_signup.go b/pkg/auth/handler/webapp/authflow_signup.go index 6828055055..4f4dcf820f 100644 --- a/pkg/auth/handler/webapp/authflow_signup.go +++ b/pkg/auth/handler/webapp/authflow_signup.go @@ -5,6 +5,8 @@ import ( "net/http" "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" @@ -13,7 +15,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowSignupHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go index 1903ed9267..6c92fd168a 100644 --- a/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go +++ b/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" @@ -14,7 +16,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowV2SignupHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/login.go b/pkg/auth/handler/webapp/authflowv2/login.go index 3de555bbea..2a5d4ddb94 100644 --- a/pkg/auth/handler/webapp/authflowv2/login.go +++ b/pkg/auth/handler/webapp/authflowv2/login.go @@ -6,6 +6,8 @@ import ( "net/http" "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" v2viewmodels "github.com/authgear/authgear-server/pkg/auth/handler/webapp/authflowv2/viewmodels" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" @@ -16,7 +18,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/template" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var TemplateWebAuthflowLoginHTML = template.RegisterHTML( diff --git a/pkg/auth/handler/webapp/authflowv2/promote.go b/pkg/auth/handler/webapp/authflowv2/promote.go index 691d2852bb..4def6c608b 100644 --- a/pkg/auth/handler/webapp/authflowv2/promote.go +++ b/pkg/auth/handler/webapp/authflowv2/promote.go @@ -4,13 +4,14 @@ import ( "net/http" "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels" "github.com/authgear/authgear-server/pkg/auth/webapp" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/util/httproute" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) var AuthflowPromoteLoginIDSchema = validation.NewSimpleSchema(` diff --git a/pkg/auth/handler/webapp/identity_test.go b/pkg/auth/handler/webapp/identity_test.go index b2a6f666bd..7978879a3b 100644 --- a/pkg/auth/handler/webapp/identity_test.go +++ b/pkg/auth/handler/webapp/identity_test.go @@ -5,10 +5,11 @@ import ( . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/auth/handler/webapp" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func TestIdentitiesDisplayName(t *testing.T) { diff --git a/pkg/auth/handler/webapp/user_profile_test.go b/pkg/auth/handler/webapp/user_profile_test.go index c00dd3807e..f9f1c676dd 100644 --- a/pkg/auth/handler/webapp/user_profile_test.go +++ b/pkg/auth/handler/webapp/user_profile_test.go @@ -3,9 +3,10 @@ package webapp import ( "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" - . "github.com/smartystreets/goconvey/convey" ) func makeFull() *model.User { diff --git a/pkg/auth/handler/webapp/viewmodels/authflow_branch_test.go b/pkg/auth/handler/webapp/viewmodels/authflow_branch_test.go index 93eadec491..67102fb2a8 100644 --- a/pkg/auth/handler/webapp/viewmodels/authflow_branch_test.go +++ b/pkg/auth/handler/webapp/viewmodels/authflow_branch_test.go @@ -3,8 +3,9 @@ package viewmodels import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func TestAuthflowBranchViewModel(t *testing.T) { diff --git a/pkg/auth/handler/webapp/wire.go b/pkg/auth/handler/webapp/wire.go index 80e8ba1799..fbff236af7 100644 --- a/pkg/auth/handler/webapp/wire.go +++ b/pkg/auth/handler/webapp/wire.go @@ -4,11 +4,12 @@ package webapp import ( + "github.com/google/wire" + "github.com/authgear/authgear-server/pkg/auth/webapp" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/redis/appredis" "github.com/authgear/authgear-server/pkg/util/clock" - "github.com/google/wire" ) func newGlobalSessionService(appID config.AppID, clock clock.Clock, redisHandle *appredis.Handle) *GlobalSessionService { diff --git a/pkg/auth/webapp/dynamic_csp_middleware_test.go b/pkg/auth/webapp/dynamic_csp_middleware_test.go index 00aa716d50..c95bc6409c 100644 --- a/pkg/auth/webapp/dynamic_csp_middleware_test.go +++ b/pkg/auth/webapp/dynamic_csp_middleware_test.go @@ -5,9 +5,10 @@ import ( "net/http/httptest" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" gomock "github.com/golang/mock/gomock" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func TestDynamicCSPMiddleware(t *testing.T) { diff --git a/pkg/lib/analytic/appdb_store.go b/pkg/lib/analytic/appdb_store.go index 4ce6326cf1..5b015f1d02 100644 --- a/pkg/lib/analytic/appdb_store.go +++ b/pkg/lib/analytic/appdb_store.go @@ -5,6 +5,7 @@ import ( "time" sq "github.com/Masterminds/squirrel" + "github.com/authgear/authgear-server/pkg/lib/infra/db/appdb" ) diff --git a/pkg/lib/analytic/chart_service_test.go b/pkg/lib/analytic/chart_service_test.go index a13e6c4fe4..5a7ca641ec 100644 --- a/pkg/lib/analytic/chart_service_test.go +++ b/pkg/lib/analytic/chart_service_test.go @@ -4,12 +4,13 @@ import ( "testing" "time" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/analytic" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/periodical" "github.com/authgear/authgear-server/pkg/util/timeutil" - . "github.com/smartystreets/goconvey/convey" ) func TestChartService(t *testing.T) { diff --git a/pkg/lib/analytic/time_test.go b/pkg/lib/analytic/time_test.go index e131544e4f..cc79a5a82c 100644 --- a/pkg/lib/analytic/time_test.go +++ b/pkg/lib/analytic/time_test.go @@ -4,9 +4,10 @@ import ( "testing" "time" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/analytic" "github.com/authgear/authgear-server/pkg/util/periodical" - . "github.com/smartystreets/goconvey/convey" ) func TestGetDateListByRangeInclusive(t *testing.T) { diff --git a/pkg/lib/authenticationflow/allowlist_test.go b/pkg/lib/authenticationflow/allowlist_test.go index 5c10f65540..84a3e3764c 100644 --- a/pkg/lib/authenticationflow/allowlist_test.go +++ b/pkg/lib/authenticationflow/allowlist_test.go @@ -3,8 +3,9 @@ package authenticationflow import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func TestFlowAllowlist(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/input_login_flow_step_authenticate_test.go b/pkg/lib/authenticationflow/declarative/input_login_flow_step_authenticate_test.go index db1650f435..731a624556 100644 --- a/pkg/lib/authenticationflow/declarative/input_login_flow_step_authenticate_test.go +++ b/pkg/lib/authenticationflow/declarative/input_login_flow_step_authenticate_test.go @@ -4,9 +4,10 @@ import ( "encoding/json" "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" - . "github.com/smartystreets/goconvey/convey" ) func TestInputSchemaLoginFlowStepAuthenticate(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/input_step_identify.go b/pkg/lib/authenticationflow/declarative/input_step_identify.go index 370854e43d..ce5e42b2eb 100644 --- a/pkg/lib/authenticationflow/declarative/input_step_identify.go +++ b/pkg/lib/authenticationflow/declarative/input_step_identify.go @@ -5,10 +5,11 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type InputSchemaStepIdentify struct { diff --git a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go index 3af96120db..2898c3f4f0 100644 --- a/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go +++ b/pkg/lib/authenticationflow/declarative/input_take_oauth_authorization_request.go @@ -5,10 +5,11 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type InputSchemaTakeOAuthAuthorizationRequest struct { diff --git a/pkg/lib/authenticationflow/declarative/input_take_oob_otp_channel_test.go b/pkg/lib/authenticationflow/declarative/input_take_oob_otp_channel_test.go index 60b5c34607..0d488be26e 100644 --- a/pkg/lib/authenticationflow/declarative/input_take_oob_otp_channel_test.go +++ b/pkg/lib/authenticationflow/declarative/input_take_oob_otp_channel_test.go @@ -4,9 +4,10 @@ import ( "encoding/json" "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/util/validation" - . "github.com/smartystreets/goconvey/convey" ) func TestInputSchemaTakeOOBOTPChannel(t *testing.T) { diff --git a/pkg/lib/authenticationflow/declarative/intent_account_linking.go b/pkg/lib/authenticationflow/declarative/intent_account_linking.go index f6fec6c8d2..0fd6e35a60 100644 --- a/pkg/lib/authenticationflow/declarative/intent_account_linking.go +++ b/pkg/lib/authenticationflow/declarative/intent_account_linking.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/model" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" @@ -12,7 +14,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/infra/mail" "github.com/authgear/authgear-server/pkg/util/phone" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func init() { diff --git a/pkg/lib/authenticationflow/declarative/intent_check_conflict_and_create_identity.go b/pkg/lib/authenticationflow/declarative/intent_check_conflict_and_create_identity.go index 7aed4e8c98..70ac835ec7 100644 --- a/pkg/lib/authenticationflow/declarative/intent_check_conflict_and_create_identity.go +++ b/pkg/lib/authenticationflow/declarative/intent_check_conflict_and_create_identity.go @@ -4,10 +4,11 @@ import ( "context" "fmt" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/model" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func init() { diff --git a/pkg/lib/authenticationflow/declarative/milestone.go b/pkg/lib/authenticationflow/declarative/milestone.go index a4a2fd989f..0f4bee3a86 100644 --- a/pkg/lib/authenticationflow/declarative/milestone.go +++ b/pkg/lib/authenticationflow/declarative/milestone.go @@ -4,6 +4,8 @@ import ( "context" "sort" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/identity" @@ -11,7 +13,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/session/idpsession" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func getUserID(flows authflow.Flows) (userID string, err error) { diff --git a/pkg/lib/authenticationflow/declarative/node_do_use_authenticator_password.go b/pkg/lib/authenticationflow/declarative/node_do_use_authenticator_password.go index a215e0dbf6..16df31fd2f 100644 --- a/pkg/lib/authenticationflow/declarative/node_do_use_authenticator_password.go +++ b/pkg/lib/authenticationflow/declarative/node_do_use_authenticator_password.go @@ -1,10 +1,11 @@ package declarative import ( + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func init() { diff --git a/pkg/lib/authenticationflow/declarative/synthetic_input_passkey.go b/pkg/lib/authenticationflow/declarative/synthetic_input_passkey.go index a533f652d9..ce9ef8407e 100644 --- a/pkg/lib/authenticationflow/declarative/synthetic_input_passkey.go +++ b/pkg/lib/authenticationflow/declarative/synthetic_input_passkey.go @@ -1,9 +1,10 @@ package declarative import ( + "github.com/go-webauthn/webauthn/protocol" + authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/go-webauthn/webauthn/protocol" ) type SyntheticInputPasskey struct { diff --git a/pkg/lib/authenticationflow/declarative/utils_account_linking.go b/pkg/lib/authenticationflow/declarative/utils_account_linking.go index a3a2ff6d68..c9090dd792 100644 --- a/pkg/lib/authenticationflow/declarative/utils_account_linking.go +++ b/pkg/lib/authenticationflow/declarative/utils_account_linking.go @@ -3,12 +3,13 @@ package declarative import ( "context" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/model" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func resolveAccountLinkingConfigsOAuth( diff --git a/pkg/lib/authenticationflow/declarative/utils_common.go b/pkg/lib/authenticationflow/declarative/utils_common.go index 19a148456d..e76b6237e3 100644 --- a/pkg/lib/authenticationflow/declarative/utils_common.go +++ b/pkg/lib/authenticationflow/declarative/utils_common.go @@ -5,6 +5,9 @@ import ( "errors" "fmt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" @@ -20,8 +23,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/phone" "github.com/authgear/authgear-server/pkg/util/uuid" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) func authenticatorIsDefault(deps *authflow.Dependencies, userID string, authenticatorKind model.AuthenticatorKind) (isDefault bool, err error) { diff --git a/pkg/lib/authenticationflow/dependencies.go b/pkg/lib/authenticationflow/dependencies.go index faf68ac349..c9fe325277 100644 --- a/pkg/lib/authenticationflow/dependencies.go +++ b/pkg/lib/authenticationflow/dependencies.go @@ -7,6 +7,8 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/accountmigration" @@ -31,7 +33,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/accesscontrol" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/httputil" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type IdentityService interface { diff --git a/pkg/lib/authenticationflow/flow.go b/pkg/lib/authenticationflow/flow.go index 7b5e66c5b8..b32463f7fb 100644 --- a/pkg/lib/authenticationflow/flow.go +++ b/pkg/lib/authenticationflow/flow.go @@ -4,8 +4,9 @@ import ( "fmt" "reflect" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + + "github.com/authgear/authgear-server/pkg/lib/config" ) // PublicFlow is a instantiable intent by the public. diff --git a/pkg/lib/authenticationflow/service.go b/pkg/lib/authenticationflow/service.go index 0732511c43..730d425216 100644 --- a/pkg/lib/authenticationflow/service.go +++ b/pkg/lib/authenticationflow/service.go @@ -6,10 +6,11 @@ import ( "errors" "net/http" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/log" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) //go:generate mockgen -source=service.go -destination=service_mock_test.go -package authenticationflow diff --git a/pkg/lib/authn/identity/candidate.go b/pkg/lib/authn/identity/candidate.go index 78a16b9bc2..a81872232f 100644 --- a/pkg/lib/authn/identity/candidate.go +++ b/pkg/lib/authn/identity/candidate.go @@ -1,9 +1,10 @@ package identity import ( + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Candidate map[string]interface{} diff --git a/pkg/lib/authn/identity/info.go b/pkg/lib/authn/identity/info.go index d8e1b62c27..c8161bd36b 100644 --- a/pkg/lib/authn/identity/info.go +++ b/pkg/lib/authn/identity/info.go @@ -4,9 +4,10 @@ import ( "fmt" "time" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Info struct { diff --git a/pkg/lib/authn/identity/info_test.go b/pkg/lib/authn/identity/info_test.go index a016c1e702..b49470e6cb 100644 --- a/pkg/lib/authn/identity/info_test.go +++ b/pkg/lib/authn/identity/info_test.go @@ -7,8 +7,9 @@ import ( . "github.com/smartystreets/goconvey/convey" - "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + + "github.com/authgear/authgear-server/pkg/api/model" ) func TestInfoJSON(t *testing.T) { diff --git a/pkg/lib/authn/identity/loginid/provider.go b/pkg/lib/authn/identity/loginid/provider.go index 4c74475ec6..34abbae4b7 100644 --- a/pkg/lib/authn/identity/loginid/provider.go +++ b/pkg/lib/authn/identity/loginid/provider.go @@ -4,12 +4,13 @@ import ( "errors" "sort" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/uuid" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) type Provider struct { diff --git a/pkg/lib/authn/identity/oauth/store.go b/pkg/lib/authn/identity/oauth/store.go index 75f7ca8569..c5bd048b20 100644 --- a/pkg/lib/authn/identity/oauth/store.go +++ b/pkg/lib/authn/identity/oauth/store.go @@ -9,12 +9,13 @@ import ( "github.com/iawaknahc/jsonschema/pkg/jsonpointer" "github.com/lib/pq" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/lib/infra/db/appdb" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type Store struct { diff --git a/pkg/lib/authn/identity/service/service_test.go b/pkg/lib/authn/identity/service/service_test.go index 9208742959..5e198491e6 100644 --- a/pkg/lib/authn/identity/service/service_test.go +++ b/pkg/lib/authn/identity/service/service_test.go @@ -7,11 +7,12 @@ import ( "github.com/golang/mock/gomock" . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func newBool(b bool) *bool { diff --git a/pkg/lib/authn/otp/code.go b/pkg/lib/authn/otp/code.go index 6fc2fef162..f12bdca0b3 100644 --- a/pkg/lib/authn/otp/code.go +++ b/pkg/lib/authn/otp/code.go @@ -3,8 +3,9 @@ package otp import ( "time" - "github.com/authgear/authgear-server/pkg/util/duration" "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + + "github.com/authgear/authgear-server/pkg/util/duration" ) const ( diff --git a/pkg/lib/authn/otp/service.go b/pkg/lib/authn/otp/service.go index e2092b1986..768ac6e4cb 100644 --- a/pkg/lib/authn/otp/service.go +++ b/pkg/lib/authn/otp/service.go @@ -4,12 +4,13 @@ import ( "errors" "time" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/ratelimit" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/httputil" "github.com/authgear/authgear-server/pkg/util/log" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) type GenerateOptions struct { diff --git a/pkg/lib/authn/sso/deps.go b/pkg/lib/authn/sso/deps.go index e29bacb6a5..d14d125b3a 100644 --- a/pkg/lib/authn/sso/deps.go +++ b/pkg/lib/authn/sso/deps.go @@ -7,8 +7,9 @@ import ( "net/url" "os" - "github.com/authgear/authgear-server/pkg/lib/config" "github.com/google/wire" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func ProvideOAuthHTTPClient(env *config.EnvironmentConfig) OAuthHTTPClient { diff --git a/pkg/lib/authn/sso/oauth_provider.go b/pkg/lib/authn/sso/oauth_provider.go index 1c131ef5c6..834baf1a06 100644 --- a/pkg/lib/authn/sso/oauth_provider.go +++ b/pkg/lib/authn/sso/oauth_provider.go @@ -1,11 +1,12 @@ package sso import ( + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type StandardAttributesNormalizer interface { diff --git a/pkg/lib/cloudstorage/azure.go b/pkg/lib/cloudstorage/azure.go index f000599508..11e2857b83 100644 --- a/pkg/lib/cloudstorage/azure.go +++ b/pkg/lib/cloudstorage/azure.go @@ -7,6 +7,7 @@ import ( "time" "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/authgear/authgear-server/pkg/util/clock" ) diff --git a/pkg/lib/cloudstorage/gcs.go b/pkg/lib/cloudstorage/gcs.go index 083affa5f2..a9157300b3 100644 --- a/pkg/lib/cloudstorage/gcs.go +++ b/pkg/lib/cloudstorage/gcs.go @@ -10,9 +10,10 @@ import ( "strings" "cloud.google.com/go/storage" - "github.com/authgear/authgear-server/pkg/util/clock" "google.golang.org/api/option" raw "google.golang.org/api/storage/v1" + + "github.com/authgear/authgear-server/pkg/util/clock" ) type GCSStorage struct { diff --git a/pkg/lib/config/feature_identity.go b/pkg/lib/config/feature_identity.go index 4d0b69823b..b5f1fe985e 100644 --- a/pkg/lib/config/feature_identity.go +++ b/pkg/lib/config/feature_identity.go @@ -1,8 +1,9 @@ package config import ( - liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + + liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" ) var _ = FeatureConfigSchema.Add("IdentityFeatureConfig", ` diff --git a/pkg/lib/config/feature_test.go b/pkg/lib/config/feature_test.go index 138a667ef3..eeed479a00 100644 --- a/pkg/lib/config/feature_test.go +++ b/pkg/lib/config/feature_test.go @@ -7,10 +7,11 @@ import ( "os" "testing" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" goyaml "gopkg.in/yaml.v2" "sigs.k8s.io/yaml" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func TestParseFeatureConfig(t *testing.T) { diff --git a/pkg/lib/config/identity.go b/pkg/lib/config/identity.go index b83ac15aa2..a50660b507 100644 --- a/pkg/lib/config/identity.go +++ b/pkg/lib/config/identity.go @@ -1,8 +1,9 @@ package config import ( - "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + + "github.com/authgear/authgear-server/pkg/api/model" ) var _ = Schema.Add("IdentityConfig", ` diff --git a/pkg/lib/config/rate_limits_env_test.go b/pkg/lib/config/rate_limits_env_test.go index ed59ce698b..238357164b 100644 --- a/pkg/lib/config/rate_limits_env_test.go +++ b/pkg/lib/config/rate_limits_env_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/lib/config" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/config" ) func TestParseRateLimitsEnv(t *testing.T) { diff --git a/pkg/lib/config/secret_data.go b/pkg/lib/config/secret_data.go index a0a74aa8b0..64fb631357 100644 --- a/pkg/lib/config/secret_data.go +++ b/pkg/lib/config/secret_data.go @@ -3,9 +3,10 @@ package config import ( "encoding/json" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/authgear/authgear-server/pkg/util/jwkutil" "github.com/authgear/authgear-server/pkg/util/slice" - "github.com/lestrrat-go/jwx/v2/jwk" ) var _ = SecretConfigSchema.Add("DatabaseCredentials", ` diff --git a/pkg/lib/config/secret_update_instruction_context.go b/pkg/lib/config/secret_update_instruction_context.go index c2894982f1..4faab5e801 100644 --- a/pkg/lib/config/secret_update_instruction_context.go +++ b/pkg/lib/config/secret_update_instruction_context.go @@ -4,8 +4,9 @@ import ( mathrand "math/rand" "time" - "github.com/authgear/authgear-server/pkg/util/clock" "github.com/lestrrat-go/jwx/v2/jwk" + + "github.com/authgear/authgear-server/pkg/util/clock" ) type SecretConfigUpdateInstructionContext struct { diff --git a/pkg/lib/config/secret_update_instruction_test.go b/pkg/lib/config/secret_update_instruction_test.go index 4253e744a5..6b5f31c421 100644 --- a/pkg/lib/config/secret_update_instruction_test.go +++ b/pkg/lib/config/secret_update_instruction_test.go @@ -13,11 +13,12 @@ import ( goyaml "gopkg.in/yaml.v2" "sigs.k8s.io/yaml" + "github.com/lestrrat-go/jwx/v2/jwk" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/jwkutil" - "github.com/lestrrat-go/jwx/v2/jwk" - . "github.com/smartystreets/goconvey/convey" ) func TestSecretConfigUpdateInstruction(t *testing.T) { diff --git a/pkg/lib/db/util/dump.go b/pkg/lib/db/util/dump.go index 8871d82e84..3dc28ab3bf 100644 --- a/pkg/lib/db/util/dump.go +++ b/pkg/lib/db/util/dump.go @@ -9,9 +9,10 @@ import ( "path/filepath" "time" + "github.com/lib/pq" + "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/util/log" - "github.com/lib/pq" ) type Dumper struct { diff --git a/pkg/lib/facade/identity.go b/pkg/lib/facade/identity.go index 1496d5815d..738dd1817a 100644 --- a/pkg/lib/facade/identity.go +++ b/pkg/lib/facade/identity.go @@ -1,8 +1,9 @@ package facade import ( - "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + + "github.com/authgear/authgear-server/pkg/lib/authn/identity" ) type IdentityFacade struct { diff --git a/pkg/lib/feature/forgotpassword/service.go b/pkg/lib/feature/forgotpassword/service.go index bb889da18c..ea740b7c21 100644 --- a/pkg/lib/feature/forgotpassword/service.go +++ b/pkg/lib/feature/forgotpassword/service.go @@ -4,6 +4,8 @@ import ( "errors" "strings" + "github.com/iawaknahc/jsonschema/pkg/jsonpointer" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" @@ -14,7 +16,6 @@ import ( "github.com/authgear/authgear-server/pkg/lib/ratelimit" "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/log" - "github.com/iawaknahc/jsonschema/pkg/jsonpointer" ) type Logger struct{ *log.Logger } diff --git a/pkg/lib/feature/siwe/service.go b/pkg/lib/feature/siwe/service.go index 58a3975bce..c827270e3c 100644 --- a/pkg/lib/feature/siwe/service.go +++ b/pkg/lib/feature/siwe/service.go @@ -8,6 +8,8 @@ import ( "strconv" "time" + siwego "github.com/spruceid/siwe-go" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" @@ -18,7 +20,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/log" "github.com/authgear/authgear-server/pkg/util/rand" "github.com/authgear/authgear-server/pkg/util/web3" - siwego "github.com/spruceid/siwe-go" ) //go:generate mockgen -source=service.go -destination=service_mock_test.go -package siwe diff --git a/pkg/lib/feature/stdattrs/transformer_test.go b/pkg/lib/feature/stdattrs/transformer_test.go index d2515a118a..d983dbda57 100644 --- a/pkg/lib/feature/stdattrs/transformer_test.go +++ b/pkg/lib/feature/stdattrs/transformer_test.go @@ -3,10 +3,11 @@ package stdattrs import ( "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/httputil" - . "github.com/smartystreets/goconvey/convey" ) func TestPictureTransformer(t *testing.T) { diff --git a/pkg/lib/images/model_test.go b/pkg/lib/images/model_test.go index d3861e4fcb..94017248fa 100644 --- a/pkg/lib/images/model_test.go +++ b/pkg/lib/images/model_test.go @@ -3,8 +3,9 @@ package images_test import ( "testing" - "github.com/authgear/authgear-server/pkg/lib/images" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/images" ) func TestFileMetadata(t *testing.T) { diff --git a/pkg/lib/infra/whatsapp/service.go b/pkg/lib/infra/whatsapp/service.go index f4b544fa6c..9a2e220d0d 100644 --- a/pkg/lib/infra/whatsapp/service.go +++ b/pkg/lib/infra/whatsapp/service.go @@ -6,10 +6,11 @@ import ( "fmt" "strings" + "github.com/sirupsen/logrus" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/intl" "github.com/authgear/authgear-server/pkg/util/log" - "github.com/sirupsen/logrus" ) type ServiceLogger struct{ *log.Logger } diff --git a/pkg/lib/interaction/context.go b/pkg/lib/interaction/context.go index 2e3a41526d..71758d24df 100644 --- a/pkg/lib/interaction/context.go +++ b/pkg/lib/interaction/context.go @@ -5,6 +5,8 @@ import ( "net/url" "time" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/event" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/authenticationinfo" @@ -30,7 +32,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/accesscontrol" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/httputil" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type IdentityService interface { diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go index 80be423718..13f732ce7c 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_provider.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_provider.go @@ -3,13 +3,14 @@ package nodes import ( "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/crypto" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go index 60431f0ffe..db7b4c8060 100644 --- a/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go +++ b/pkg/lib/interaction/nodes/use_identity_oauth_user_info.go @@ -4,12 +4,13 @@ import ( "crypto/subtle" "fmt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/identity" "github.com/authgear/authgear-server/pkg/lib/interaction" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauth/handler/handler_proxy_redirect_test.go b/pkg/lib/oauth/handler/handler_proxy_redirect_test.go index 8696070d2f..7cc84c5ff0 100644 --- a/pkg/lib/oauth/handler/handler_proxy_redirect_test.go +++ b/pkg/lib/oauth/handler/handler_proxy_redirect_test.go @@ -3,9 +3,10 @@ package handler_test import ( "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauth/handler" - . "github.com/smartystreets/goconvey/convey" ) func TestProxyRedirectHandler(t *testing.T) { diff --git a/pkg/lib/oauthrelyingparty/adfs/provider.go b/pkg/lib/oauthrelyingparty/adfs/provider.go index 654e0255ce..0526a6a341 100644 --- a/pkg/lib/oauthrelyingparty/adfs/provider.go +++ b/pkg/lib/oauthrelyingparty/adfs/provider.go @@ -3,11 +3,12 @@ package adfs import ( "context" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/apple/provider.go b/pkg/lib/oauthrelyingparty/apple/provider.go index 81b8ff8eb2..9adeec1051 100644 --- a/pkg/lib/oauthrelyingparty/apple/provider.go +++ b/pkg/lib/oauthrelyingparty/apple/provider.go @@ -8,6 +8,8 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" @@ -15,7 +17,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwtutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go index 7996192b5c..9d3471761e 100644 --- a/pkg/lib/oauthrelyingparty/azureadb2c/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadb2c/provider.go @@ -4,11 +4,12 @@ import ( "context" "fmt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/azureadv2/provider.go b/pkg/lib/oauthrelyingparty/azureadv2/provider.go index 2dd12f762e..3b3ddad909 100644 --- a/pkg/lib/oauthrelyingparty/azureadv2/provider.go +++ b/pkg/lib/oauthrelyingparty/azureadv2/provider.go @@ -4,11 +4,12 @@ import ( "context" "fmt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/builtin.go b/pkg/lib/oauthrelyingparty/builtin.go index 25e706ad76..a568990fd7 100644 --- a/pkg/lib/oauthrelyingparty/builtin.go +++ b/pkg/lib/oauthrelyingparty/builtin.go @@ -1,8 +1,9 @@ package oauthrelyingparty import ( - "github.com/authgear/authgear-server/pkg/util/validation" "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + + "github.com/authgear/authgear-server/pkg/util/validation" ) const ( diff --git a/pkg/lib/oauthrelyingparty/facebook/provider.go b/pkg/lib/oauthrelyingparty/facebook/provider.go index ecbf5fe4e2..97af0fbd0f 100644 --- a/pkg/lib/oauthrelyingparty/facebook/provider.go +++ b/pkg/lib/oauthrelyingparty/facebook/provider.go @@ -3,12 +3,13 @@ package facebook import ( "net/url" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/crypto" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/github/provider.go b/pkg/lib/oauthrelyingparty/github/provider.go index c9f535d90c..722563d68a 100644 --- a/pkg/lib/oauthrelyingparty/github/provider.go +++ b/pkg/lib/oauthrelyingparty/github/provider.go @@ -7,13 +7,14 @@ import ( "net/url" "strings" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/errorutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/google/provider.go b/pkg/lib/oauthrelyingparty/google/provider.go index 014c8b1a2b..a0cf9c15cc 100644 --- a/pkg/lib/oauthrelyingparty/google/provider.go +++ b/pkg/lib/oauthrelyingparty/google/provider.go @@ -3,11 +3,12 @@ package google import ( "context" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/linkedin/provider.go b/pkg/lib/oauthrelyingparty/linkedin/provider.go index 4417499bce..1346811bed 100644 --- a/pkg/lib/oauthrelyingparty/linkedin/provider.go +++ b/pkg/lib/oauthrelyingparty/linkedin/provider.go @@ -1,11 +1,12 @@ package linkedin import ( + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go index b3ef718286..6e18747844 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/error.go @@ -1,8 +1,9 @@ package oauthrelyingpartyutil import ( - "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + + "github.com/authgear/authgear-server/pkg/api/apierrors" ) var InvalidConfiguration = apierrors.InternalError.WithReason("InvalidConfiguration") diff --git a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go index daf2730332..dca64c51eb 100644 --- a/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go +++ b/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil/oidc.go @@ -11,9 +11,10 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/util/duration" "github.com/authgear/authgear-server/pkg/util/jwsutil" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) type jwtClock struct { diff --git a/pkg/lib/oauthrelyingparty/wechat/provider.go b/pkg/lib/oauthrelyingparty/wechat/provider.go index 30b63fa38b..54ebd38212 100644 --- a/pkg/lib/oauthrelyingparty/wechat/provider.go +++ b/pkg/lib/oauthrelyingparty/wechat/provider.go @@ -9,11 +9,12 @@ import ( "net/url" "strconv" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" + "github.com/authgear/authgear-server/pkg/lib/authn/stdattrs" liboauthrelyingparty "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty" "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/oauthrelyingpartyutil" "github.com/authgear/authgear-server/pkg/util/validation" - "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" ) func init() { diff --git a/pkg/lib/rolesgroups/store_group_user.go b/pkg/lib/rolesgroups/store_group_user.go index 844e61c5bd..96e0425448 100644 --- a/pkg/lib/rolesgroups/store_group_user.go +++ b/pkg/lib/rolesgroups/store_group_user.go @@ -1,12 +1,13 @@ package rolesgroups import ( + "github.com/lib/pq" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/slice" "github.com/authgear/authgear-server/pkg/util/uuid" - "github.com/lib/pq" ) func (s *Store) ListGroupsByUserIDs(userIDs []string) (map[string][]*Group, error) { diff --git a/pkg/lib/rolesgroups/store_role_user.go b/pkg/lib/rolesgroups/store_role_user.go index 40dbe485e2..890bbb4521 100644 --- a/pkg/lib/rolesgroups/store_role_user.go +++ b/pkg/lib/rolesgroups/store_role_user.go @@ -3,13 +3,14 @@ package rolesgroups import ( "sort" + "github.com/lib/pq" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/lib/infra/db" "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/setutil" "github.com/authgear/authgear-server/pkg/util/slice" "github.com/authgear/authgear-server/pkg/util/uuid" - "github.com/lib/pq" ) func (s *Store) ListRolesByUserIDs(userIDs []string) (map[string][]*Role, error) { diff --git a/pkg/lib/rolesgroups/store_user.go b/pkg/lib/rolesgroups/store_user.go index 228563d33e..5bce2d4f38 100644 --- a/pkg/lib/rolesgroups/store_user.go +++ b/pkg/lib/rolesgroups/store_user.go @@ -4,9 +4,10 @@ import ( "database/sql" "errors" + "github.com/lib/pq" + "github.com/authgear/authgear-server/pkg/api" "github.com/authgear/authgear-server/pkg/lib/infra/db" - "github.com/lib/pq" ) func (s *Store) scanUserID(scanner db.Scanner) (string, error) { diff --git a/pkg/lib/sessionlisting/listing_test.go b/pkg/lib/sessionlisting/listing_test.go index 54689f92f6..3ff5d78ef9 100644 --- a/pkg/lib/sessionlisting/listing_test.go +++ b/pkg/lib/sessionlisting/listing_test.go @@ -4,14 +4,15 @@ import ( "testing" "time" + gomock "github.com/golang/mock/gomock" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/oauth" "github.com/authgear/authgear-server/pkg/lib/session" "github.com/authgear/authgear-server/pkg/lib/session/access" "github.com/authgear/authgear-server/pkg/lib/session/idpsession" "github.com/authgear/authgear-server/pkg/lib/sessionlisting" - gomock "github.com/golang/mock/gomock" - . "github.com/smartystreets/goconvey/convey" ) func makeDeviceInfo(deviceName string, deviceModel string) map[string]interface{} { diff --git a/pkg/lib/web/embedded_resource_test.go b/pkg/lib/web/embedded_resource_test.go index b28b2949bc..7526b3af48 100644 --- a/pkg/lib/web/embedded_resource_test.go +++ b/pkg/lib/web/embedded_resource_test.go @@ -7,8 +7,9 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/lib/web" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/lib/web" ) func TestGlobalEmbeddedResourceManager(t *testing.T) { diff --git a/pkg/lib/workflow/event_store.go b/pkg/lib/workflow/event_store.go index 2a2f6d16c6..13affd763c 100644 --- a/pkg/lib/workflow/event_store.go +++ b/pkg/lib/workflow/event_store.go @@ -5,10 +5,11 @@ import ( "errors" "fmt" + goredis "github.com/go-redis/redis/v8" + "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/infra/redis/appredis" "github.com/authgear/authgear-server/pkg/util/pubsub" - goredis "github.com/go-redis/redis/v8" ) type eventRedisPool struct{ *appredis.Handle } diff --git a/pkg/portal/appresource/util.go b/pkg/portal/appresource/util.go index 6d70154911..0beaf4ec91 100644 --- a/pkg/portal/appresource/util.go +++ b/pkg/portal/appresource/util.go @@ -4,8 +4,9 @@ import ( "io/ioutil" "path" - "github.com/authgear/authgear-server/pkg/util/resource" "github.com/spf13/afero" + + "github.com/authgear/authgear-server/pkg/util/resource" ) func cloneFS(fs resource.Fs) (afero.Fs, error) { diff --git a/pkg/portal/graphql/billing.go b/pkg/portal/graphql/billing.go index cb8ab1e981..b95df0be14 100644 --- a/pkg/portal/graphql/billing.go +++ b/pkg/portal/graphql/billing.go @@ -1,8 +1,9 @@ package graphql import ( - "github.com/authgear/authgear-server/pkg/portal/model" "github.com/graphql-go/graphql" + + "github.com/authgear/authgear-server/pkg/portal/model" ) var priceType = graphql.NewEnum(graphql.EnumConfig{ diff --git a/pkg/portal/graphql/billing_mutation.go b/pkg/portal/graphql/billing_mutation.go index cc159d2f7a..f5edfb1549 100644 --- a/pkg/portal/graphql/billing_mutation.go +++ b/pkg/portal/graphql/billing_mutation.go @@ -3,14 +3,15 @@ package graphql import ( "errors" + relay "github.com/authgear/graphql-go-relay" + "github.com/graphql-go/graphql" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/api/event/nonblocking" "github.com/authgear/authgear-server/pkg/portal/model" "github.com/authgear/authgear-server/pkg/portal/service" "github.com/authgear/authgear-server/pkg/portal/session" "github.com/authgear/authgear-server/pkg/util/graphqlutil" - relay "github.com/authgear/graphql-go-relay" - "github.com/graphql-go/graphql" ) var createCheckoutSessionInput = graphql.NewInputObject(graphql.InputObjectConfig{ diff --git a/pkg/portal/graphql/chart.go b/pkg/portal/graphql/chart.go index 76c4617edb..6127c0c2d0 100644 --- a/pkg/portal/graphql/chart.go +++ b/pkg/portal/graphql/chart.go @@ -4,10 +4,11 @@ import ( "errors" "time" - "github.com/authgear/authgear-server/pkg/api/apierrors" - "github.com/authgear/authgear-server/pkg/util/graphqlutil" relay "github.com/authgear/graphql-go-relay" "github.com/graphql-go/graphql" + + "github.com/authgear/authgear-server/pkg/api/apierrors" + "github.com/authgear/authgear-server/pkg/util/graphqlutil" ) var periodicalEnum = graphql.NewEnum(graphql.EnumConfig{ diff --git a/pkg/portal/graphql/context.go b/pkg/portal/graphql/context.go index b27362e625..249247bfd0 100644 --- a/pkg/portal/graphql/context.go +++ b/pkg/portal/graphql/context.go @@ -5,6 +5,8 @@ import ( "net/http" "time" + "github.com/stripe/stripe-go/v72" + "github.com/authgear/authgear-server/pkg/api/event" apimodel "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/analytic" @@ -19,7 +21,6 @@ import ( "github.com/authgear/authgear-server/pkg/util/graphqlutil" "github.com/authgear/authgear-server/pkg/util/log" "github.com/authgear/authgear-server/pkg/util/web3" - "github.com/stripe/stripe-go/v72" ) type UserLoader interface { diff --git a/pkg/portal/graphql/nodes.go b/pkg/portal/graphql/nodes.go index 957fe61f7d..5bb51bf708 100644 --- a/pkg/portal/graphql/nodes.go +++ b/pkg/portal/graphql/nodes.go @@ -5,9 +5,10 @@ import ( "fmt" "reflect" - "github.com/authgear/authgear-server/pkg/portal/session" relay "github.com/authgear/graphql-go-relay" "github.com/graphql-go/graphql" + + "github.com/authgear/authgear-server/pkg/portal/session" ) type NodeResolver func(ctx context.Context, id string) (interface{}, error) diff --git a/pkg/portal/service/kubernetes.go b/pkg/portal/service/kubernetes.go index df04396272..3c78a1d709 100644 --- a/pkg/portal/service/kubernetes.go +++ b/pkg/portal/service/kubernetes.go @@ -24,10 +24,11 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" + certmanagerclientset "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" + portalconfig "github.com/authgear/authgear-server/pkg/portal/config" "github.com/authgear/authgear-server/pkg/util/kubeutil" "github.com/authgear/authgear-server/pkg/util/log" - certmanagerclientset "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" ) var LabelAppID = "authgear.com/app-id" diff --git a/pkg/portal/service/subscription.go b/pkg/portal/service/subscription.go index 652f82a715..dd835f0dc8 100644 --- a/pkg/portal/service/subscription.go +++ b/pkg/portal/service/subscription.go @@ -8,6 +8,7 @@ import ( "sigs.k8s.io/yaml" "github.com/Masterminds/squirrel" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/lib/config/configsource" "github.com/authgear/authgear-server/pkg/lib/infra/db/globaldb" diff --git a/pkg/util/hexstring/hexstring_test.go b/pkg/util/hexstring/hexstring_test.go index 48766bc2c2..6bb486bfce 100644 --- a/pkg/util/hexstring/hexstring_test.go +++ b/pkg/util/hexstring/hexstring_test.go @@ -4,8 +4,9 @@ import ( "math/big" "testing" - "github.com/authgear/authgear-server/pkg/util/hexstring" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/hexstring" ) func TestHexCmp(t *testing.T) { diff --git a/pkg/util/httpsigning/httpsigning_test.go b/pkg/util/httpsigning/httpsigning_test.go index 3794764416..507b191154 100644 --- a/pkg/util/httpsigning/httpsigning_test.go +++ b/pkg/util/httpsigning/httpsigning_test.go @@ -5,8 +5,9 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/util/httputil" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/httputil" ) func TestHTTPSigning(t *testing.T) { diff --git a/pkg/util/httputil/json_test.go b/pkg/util/httputil/json_test.go index 66ab28a30e..5b48dd7c55 100644 --- a/pkg/util/httputil/json_test.go +++ b/pkg/util/httputil/json_test.go @@ -6,9 +6,10 @@ import ( "strings" "testing" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/api/apierrors" "github.com/authgear/authgear-server/pkg/util/validation" - . "github.com/smartystreets/goconvey/convey" ) func TestBindJSONBody(t *testing.T) { diff --git a/pkg/util/matchlist/matchlist_test.go b/pkg/util/matchlist/matchlist_test.go index c630450bbe..5721fec2ed 100644 --- a/pkg/util/matchlist/matchlist_test.go +++ b/pkg/util/matchlist/matchlist_test.go @@ -3,8 +3,9 @@ package matchlist_test import ( "testing" - "github.com/authgear/authgear-server/pkg/util/matchlist" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/matchlist" ) func TestMatchList(t *testing.T) { diff --git a/pkg/util/periodical/periodical_test.go b/pkg/util/periodical/periodical_test.go index b217891289..768cb7445d 100644 --- a/pkg/util/periodical/periodical_test.go +++ b/pkg/util/periodical/periodical_test.go @@ -4,9 +4,10 @@ import ( "testing" "time" + . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/periodical" - . "github.com/smartystreets/goconvey/convey" ) func TestPeriodicalArgumentParser(t *testing.T) { diff --git a/pkg/util/timeutil/isoweek_test.go b/pkg/util/timeutil/isoweek_test.go index 91f35df87f..84966c89b7 100644 --- a/pkg/util/timeutil/isoweek_test.go +++ b/pkg/util/timeutil/isoweek_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/util/timeutil" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/timeutil" ) func TestFirstDayOfISOWeek(t *testing.T) { diff --git a/pkg/util/timeutil/truncate_test.go b/pkg/util/timeutil/truncate_test.go index a3038fb225..f099d9588b 100644 --- a/pkg/util/timeutil/truncate_test.go +++ b/pkg/util/timeutil/truncate_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/authgear/authgear-server/pkg/util/timeutil" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/timeutil" ) func TestDateUtil(t *testing.T) { diff --git a/pkg/util/web3/contractid_test.go b/pkg/util/web3/contractid_test.go index 193440845a..fa039d3731 100644 --- a/pkg/util/web3/contractid_test.go +++ b/pkg/util/web3/contractid_test.go @@ -5,8 +5,9 @@ import ( "net/url" "testing" - "github.com/authgear/authgear-server/pkg/util/web3" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/web3" ) func TestNew(t *testing.T) { diff --git a/pkg/util/web3/eip55.go b/pkg/util/web3/eip55.go index 7bae0fa1a0..ade5f3eea9 100644 --- a/pkg/util/web3/eip55.go +++ b/pkg/util/web3/eip55.go @@ -4,8 +4,9 @@ import ( "fmt" "regexp" - "github.com/authgear/authgear-server/pkg/util/hexstring" "github.com/ethereum/go-ethereum/common" + + "github.com/authgear/authgear-server/pkg/util/hexstring" ) // https://eips.ethereum.org/EIPS/eip-55 diff --git a/pkg/util/web3/eip681_test.go b/pkg/util/web3/eip681_test.go index 0f10ead800..4c1ef3b032 100644 --- a/pkg/util/web3/eip681_test.go +++ b/pkg/util/web3/eip681_test.go @@ -5,8 +5,9 @@ import ( "net/url" "testing" - "github.com/authgear/authgear-server/pkg/util/web3" . "github.com/smartystreets/goconvey/convey" + + "github.com/authgear/authgear-server/pkg/util/web3" ) func TestEIP681(t *testing.T) { From 7ce197506f3387f97c93ba36b0f60c7c94054f01 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Tue, 14 May 2024 12:14:58 +0800 Subject: [PATCH 28/30] Fix compilation errors after rebase --- pkg/auth/handler/webapp/authflowv2/account_linking.go | 2 +- .../declarative/data_account_linking.go | 2 +- .../input_account_linking_identification.go | 10 +++++----- .../authenticationflow/declarative/input_interface.go | 2 +- .../declarative/intent_account_linking.go | 4 ++-- pkg/lib/authenticationflow/declarative/milestone.go | 3 +-- .../node_use_account_linking_identification.go | 7 +++---- .../synthetic_input_account_linking_identify.go | 5 ++--- .../declarative/utils_account_linking.go | 2 +- 9 files changed, 17 insertions(+), 20 deletions(-) diff --git a/pkg/auth/handler/webapp/authflowv2/account_linking.go b/pkg/auth/handler/webapp/authflowv2/account_linking.go index 484212bb7c..e7f939d369 100644 --- a/pkg/auth/handler/webapp/authflowv2/account_linking.go +++ b/pkg/auth/handler/webapp/authflowv2/account_linking.go @@ -39,7 +39,7 @@ func ConfigureAuthflowV2AccountLinkingRoute(route httproute.Route) httproute.Rou type AuthflowV2AccountLinkingOption struct { Identification config.AuthenticationFlowIdentification MaskedDisplayName string - ProviderType config.OAuthSSOProviderType + ProviderType string Index int } diff --git a/pkg/lib/authenticationflow/declarative/data_account_linking.go b/pkg/lib/authenticationflow/declarative/data_account_linking.go index 2d8092ceaf..43eae89a90 100644 --- a/pkg/lib/authenticationflow/declarative/data_account_linking.go +++ b/pkg/lib/authenticationflow/declarative/data_account_linking.go @@ -12,7 +12,7 @@ type AccountLinkingIdentificationOption struct { Action config.AccountLinkingAction `json:"action"` // ProviderType is specific to OAuth. - ProviderType config.OAuthSSOProviderType `json:"provider_type,omitempty"` + ProviderType string `json:"provider_type,omitempty"` // Alias is specific to OAuth. Alias string `json:"alias,omitempty"` } diff --git a/pkg/lib/authenticationflow/declarative/input_account_linking_identification.go b/pkg/lib/authenticationflow/declarative/input_account_linking_identification.go index eb191a4c9a..dfe8b2f470 100644 --- a/pkg/lib/authenticationflow/declarative/input_account_linking_identification.go +++ b/pkg/lib/authenticationflow/declarative/input_account_linking_identification.go @@ -3,10 +3,10 @@ package declarative import ( "encoding/json" + "github.com/authgear/oauthrelyingparty/pkg/api/oauthrelyingparty" "github.com/iawaknahc/jsonschema/pkg/jsonpointer" authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -43,7 +43,7 @@ func (i *InputSchemaAccountLinkingIdentification) SchemaBuilder() validation.Sch // response_mode is optional. b.Properties().Property("response_mode", validation.SchemaBuilder{}. Type(validation.TypeString). - Enum(sso.ResponseModeFormPost, sso.ResponseModeQuery)) + Enum(oauthrelyingparty.ResponseModeFormPost, oauthrelyingparty.ResponseModeQuery)) } b.Required(required...) oneOf = append(oneOf, b) @@ -70,8 +70,8 @@ func (i *InputSchemaAccountLinkingIdentification) MakeInput(rawMessage json.RawM type InputAccountLinkingIdentification struct { Index int `json:"index,omitempty"` - RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + RedirectURI string `json:"redirect_uri,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.Input = &InputAccountLinkingIdentification{} @@ -85,6 +85,6 @@ func (i *InputAccountLinkingIdentification) GetAccountLinkingIdentificationIndex func (i *InputAccountLinkingIdentification) GetAccountLinkingOAuthRedirectURI() string { return i.RedirectURI } -func (i *InputAccountLinkingIdentification) GetAccountLinkingOAuthResponseMode() sso.ResponseMode { +func (i *InputAccountLinkingIdentification) GetAccountLinkingOAuthResponseMode() string { return i.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/input_interface.go b/pkg/lib/authenticationflow/declarative/input_interface.go index 2571e40b0f..71342360d7 100644 --- a/pkg/lib/authenticationflow/declarative/input_interface.go +++ b/pkg/lib/authenticationflow/declarative/input_interface.go @@ -28,7 +28,7 @@ type inputTakeAccountRecoveryDestinationOptionIndex interface { type inputTakeAccountLinkingIdentification interface { GetAccountLinkingIdentificationIndex() int GetAccountLinkingOAuthRedirectURI() string - GetAccountLinkingOAuthResponseMode() sso.ResponseMode + GetAccountLinkingOAuthResponseMode() string } type inputTakeAuthenticationMethod interface { diff --git a/pkg/lib/authenticationflow/declarative/intent_account_linking.go b/pkg/lib/authenticationflow/declarative/intent_account_linking.go index 0fd6e35a60..b1c6a007d1 100644 --- a/pkg/lib/authenticationflow/declarative/intent_account_linking.go +++ b/pkg/lib/authenticationflow/declarative/intent_account_linking.go @@ -215,7 +215,7 @@ func (i *IntentAccountLinking) getOptions() []AccountLinkingIdentificationOption return slice.FlatMap(i.Conflicts, func(c *AccountLinkingConflict) []AccountLinkingIdentificationOptionInternal { var identifcation config.AuthenticationFlowIdentification var maskedDisplayName string - var providerType config.OAuthSSOProviderType + var providerType string var providerAlias string identity := c.Identity @@ -240,7 +240,7 @@ func (i *IntentAccountLinking) getOptions() []AccountLinkingIdentificationOption } case model.IdentityTypeOAuth: identifcation = config.AuthenticationFlowIdentificationOAuth - providerType = config.OAuthSSOProviderType(identity.OAuth.ProviderID.Type) + providerType = identity.OAuth.ProviderID.Type maskedDisplayName = identity.OAuth.GetDisplayName() providerAlias = identity.OAuth.ProviderAlias default: diff --git a/pkg/lib/authenticationflow/declarative/milestone.go b/pkg/lib/authenticationflow/declarative/milestone.go index 0f4bee3a86..469e312a5b 100644 --- a/pkg/lib/authenticationflow/declarative/milestone.go +++ b/pkg/lib/authenticationflow/declarative/milestone.go @@ -9,7 +9,6 @@ import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" "github.com/authgear/authgear-server/pkg/lib/authn/identity" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/lib/session/idpsession" "github.com/authgear/authgear-server/pkg/util/slice" @@ -258,7 +257,7 @@ type MilestoneUseAccountLinkingIdentification interface { MilestoneUseAccountLinkingIdentification() *AccountLinkingConflict MilestoneUseAccountLinkingIdentificationSelectedOption() AccountLinkingIdentificationOption MilestoneUseAccountLinkingIdentificationRedirectURI() string - MilestoneUseAccountLinkingIdentificationResponseMode() sso.ResponseMode + MilestoneUseAccountLinkingIdentificationResponseMode() string } type MilestonePromptCreatePasskey interface { diff --git a/pkg/lib/authenticationflow/declarative/node_use_account_linking_identification.go b/pkg/lib/authenticationflow/declarative/node_use_account_linking_identification.go index 97b7f0445e..5ebb360849 100644 --- a/pkg/lib/authenticationflow/declarative/node_use_account_linking_identification.go +++ b/pkg/lib/authenticationflow/declarative/node_use_account_linking_identification.go @@ -2,7 +2,6 @@ package declarative import ( authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow" - "github.com/authgear/authgear-server/pkg/lib/authn/sso" ) func init() { @@ -14,8 +13,8 @@ type NodeUseAccountLinkingIdentification struct { Conflict *AccountLinkingConflict `json:"conflict,omitempty"` // oauth - RedirectURI string `json:"redirect_uri,omitempty"` - ResponseMode sso.ResponseMode `json:"response_mode,omitempty"` + RedirectURI string `json:"redirect_uri,omitempty"` + ResponseMode string `json:"response_mode,omitempty"` } var _ authflow.NodeSimple = &NodeUseAccountLinkingIdentification{} @@ -36,6 +35,6 @@ func (n *NodeUseAccountLinkingIdentification) MilestoneUseAccountLinkingIdentifi func (n *NodeUseAccountLinkingIdentification) MilestoneUseAccountLinkingIdentificationRedirectURI() string { return n.RedirectURI } -func (n *NodeUseAccountLinkingIdentification) MilestoneUseAccountLinkingIdentificationResponseMode() sso.ResponseMode { +func (n *NodeUseAccountLinkingIdentification) MilestoneUseAccountLinkingIdentificationResponseMode() string { return n.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/synthetic_input_account_linking_identify.go b/pkg/lib/authenticationflow/declarative/synthetic_input_account_linking_identify.go index 921821cf83..0338167afc 100644 --- a/pkg/lib/authenticationflow/declarative/synthetic_input_account_linking_identify.go +++ b/pkg/lib/authenticationflow/declarative/synthetic_input_account_linking_identify.go @@ -1,7 +1,6 @@ package declarative import ( - "github.com/authgear/authgear-server/pkg/lib/authn/sso" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -15,7 +14,7 @@ type SyntheticInputAccountLinkingIdentify struct { // For identification=oauth Alias string RedirectURI string - ResponseMode sso.ResponseMode + ResponseMode string } // GetLoginID implements inputTakeLoginID. @@ -39,7 +38,7 @@ func (i *SyntheticInputAccountLinkingIdentify) GetOAuthRedirectURI() string { } // GetOAuthResponseMode implements inputTakeOAuthAuthorizationRequest. -func (i *SyntheticInputAccountLinkingIdentify) GetOAuthResponseMode() sso.ResponseMode { +func (i *SyntheticInputAccountLinkingIdentify) GetOAuthResponseMode() string { return i.ResponseMode } diff --git a/pkg/lib/authenticationflow/declarative/utils_account_linking.go b/pkg/lib/authenticationflow/declarative/utils_account_linking.go index c9090dd792..83162a2f7d 100644 --- a/pkg/lib/authenticationflow/declarative/utils_account_linking.go +++ b/pkg/lib/authenticationflow/declarative/utils_account_linking.go @@ -144,7 +144,7 @@ func linkByOAuthIncomingOAuthSpec( // Not the same type, so must be not identical continue } - if !conflict.Identity.OAuth.ProviderID.Equal(&request.Spec.OAuth.ProviderID) { + if !conflict.Identity.OAuth.ProviderID.Equal(request.Spec.OAuth.ProviderID) { // Not the same provider continue } From 9e451b6e9f5b9f719548c3f480ec8dce98d480de Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Tue, 14 May 2024 14:10:18 +0800 Subject: [PATCH 29/30] Move Normalizer to internalinterface to solve import cycle Also removed the previous import cycle workaround. --- pkg/api/internalinterface/loginid_normalizer.go | 6 ++++++ pkg/lib/authn/authenticator/oob/provider.go | 4 ++-- pkg/lib/authn/identity/candidate.go | 6 ++---- pkg/lib/authn/identity/loginid/normalizer.go | 16 ++++++++++------ .../authn/identity/loginid/normalizer_test.go | 3 ++- pkg/lib/authn/stdattrs/normalizer.go | 4 ++-- pkg/lib/authn/stdattrs/normalizer_mock_test.go | 6 +++--- 7 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 pkg/api/internalinterface/loginid_normalizer.go diff --git a/pkg/api/internalinterface/loginid_normalizer.go b/pkg/api/internalinterface/loginid_normalizer.go new file mode 100644 index 0000000000..070f231d45 --- /dev/null +++ b/pkg/api/internalinterface/loginid_normalizer.go @@ -0,0 +1,6 @@ +package internalinterface + +type LoginIDNormalizer interface { + Normalize(loginID string) (string, error) + ComputeUniqueKey(normalizeLoginID string) (string, error) +} diff --git a/pkg/lib/authn/authenticator/oob/provider.go b/pkg/lib/authn/authenticator/oob/provider.go index e51ad1371e..c012256be0 100644 --- a/pkg/lib/authn/authenticator/oob/provider.go +++ b/pkg/lib/authn/authenticator/oob/provider.go @@ -4,16 +4,16 @@ import ( "fmt" "sort" + "github.com/authgear/authgear-server/pkg/api/internalinterface" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/authn/authenticator" - "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" "github.com/authgear/authgear-server/pkg/util/clock" "github.com/authgear/authgear-server/pkg/util/uuid" "github.com/authgear/authgear-server/pkg/util/validation" ) type LoginIDNormalizerFactory interface { - NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) loginid.Normalizer + NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) internalinterface.LoginIDNormalizer } type Provider struct { diff --git a/pkg/lib/authn/identity/candidate.go b/pkg/lib/authn/identity/candidate.go index a81872232f..75a51675fe 100644 --- a/pkg/lib/authn/identity/candidate.go +++ b/pkg/lib/authn/identity/candidate.go @@ -5,6 +5,7 @@ import ( "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" + "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" ) type Candidate map[string]interface{} @@ -28,16 +29,13 @@ const ( ) func NewOAuthCandidate(cfg oauthrelyingparty.ProviderConfig) Candidate { - // Ideally, we should import oauthrelyingparty/wechat and use ProviderConfig there. - // But that will result in import cycle. - app_type, _ := cfg["app_type"].(string) return Candidate{ CandidateKeyIdentityID: "", CandidateKeyType: string(model.IdentityTypeOAuth), CandidateKeyProviderType: string(cfg.Type()), CandidateKeyProviderAlias: cfg.Alias(), CandidateKeyProviderSubjectID: "", - CandidateKeyProviderAppType: app_type, + CandidateKeyProviderAppType: string(wechat.ProviderConfig(cfg).AppType()), CandidateKeyDisplayID: "", CandidateKeyModifyDisabled: cfg.ModifyDisabled(), } diff --git a/pkg/lib/authn/identity/loginid/normalizer.go b/pkg/lib/authn/identity/loginid/normalizer.go index 34d154afde..868b605876 100644 --- a/pkg/lib/authn/identity/loginid/normalizer.go +++ b/pkg/lib/authn/identity/loginid/normalizer.go @@ -8,21 +8,17 @@ import ( "golang.org/x/text/secure/precis" "golang.org/x/text/unicode/norm" + "github.com/authgear/authgear-server/pkg/api/internalinterface" "github.com/authgear/authgear-server/pkg/api/model" "github.com/authgear/authgear-server/pkg/lib/config" "github.com/authgear/authgear-server/pkg/util/phone" ) -type Normalizer interface { - Normalize(loginID string) (string, error) - ComputeUniqueKey(normalizeLoginID string) (string, error) -} - type NormalizerFactory struct { Config *config.LoginIDConfig } -func (f *NormalizerFactory) NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) Normalizer { +func (f *NormalizerFactory) NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) internalinterface.LoginIDNormalizer { switch loginIDKeyType { case model.LoginIDKeyTypeEmail: return &EmailNormalizer{ @@ -43,6 +39,8 @@ type EmailNormalizer struct { Config *config.LoginIDEmailConfig } +var _ internalinterface.LoginIDNormalizer = &EmailNormalizer{} + func (n *EmailNormalizer) Normalize(loginID string) (string, error) { // refs from stdlib // https://golang.org/src/net/mail/message.go?s=5217:5250#L172 @@ -95,6 +93,8 @@ type UsernameNormalizer struct { Config *config.LoginIDUsernameConfig } +var _ internalinterface.LoginIDNormalizer = &UsernameNormalizer{} + func (n *UsernameNormalizer) Normalize(loginID string) (string, error) { loginID = norm.NFKC.String(loginID) @@ -117,6 +117,8 @@ func (n *UsernameNormalizer) ComputeUniqueKey(normalizeLoginID string) (string, type PhoneNumberNormalizer struct { } +var _ internalinterface.LoginIDNormalizer = &PhoneNumberNormalizer{} + func (n *PhoneNumberNormalizer) Normalize(loginID string) (string, error) { e164, err := phone.LegalParser.ParseInputPhoneNumber(loginID) if err != nil { @@ -132,6 +134,8 @@ func (n *PhoneNumberNormalizer) ComputeUniqueKey(normalizeLoginID string) (strin type NullNormalizer struct{} +var _ internalinterface.LoginIDNormalizer = &NullNormalizer{} + func (n *NullNormalizer) Normalize(loginID string) (string, error) { return loginID, nil } diff --git a/pkg/lib/authn/identity/loginid/normalizer_test.go b/pkg/lib/authn/identity/loginid/normalizer_test.go index 8210645fa5..9b56835465 100644 --- a/pkg/lib/authn/identity/loginid/normalizer_test.go +++ b/pkg/lib/authn/identity/loginid/normalizer_test.go @@ -5,6 +5,7 @@ import ( . "github.com/smartystreets/goconvey/convey" + "github.com/authgear/authgear-server/pkg/api/internalinterface" "github.com/authgear/authgear-server/pkg/lib/config" ) @@ -13,7 +14,7 @@ func TestNormalizers(t *testing.T) { LoginID string NormalizedLoginID string } - f := func(c Case, n Normalizer) { + f := func(c Case, n internalinterface.LoginIDNormalizer) { result, _ := n.Normalize(c.LoginID) So(result, ShouldEqual, c.NormalizedLoginID) } diff --git a/pkg/lib/authn/stdattrs/normalizer.go b/pkg/lib/authn/stdattrs/normalizer.go index b093799130..49154c045f 100644 --- a/pkg/lib/authn/stdattrs/normalizer.go +++ b/pkg/lib/authn/stdattrs/normalizer.go @@ -3,8 +3,8 @@ package stdattrs import ( "golang.org/x/text/language" + "github.com/authgear/authgear-server/pkg/api/internalinterface" "github.com/authgear/authgear-server/pkg/api/model" - "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" "github.com/authgear/authgear-server/pkg/util/phone" "github.com/authgear/authgear-server/pkg/util/validation" ) @@ -12,7 +12,7 @@ import ( //go:generate mockgen -source=normalizer.go -destination=normalizer_mock_test.go -package stdattrs type LoginIDNormalizerFactory interface { - NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) loginid.Normalizer + NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) internalinterface.LoginIDNormalizer } type Normalizer struct { diff --git a/pkg/lib/authn/stdattrs/normalizer_mock_test.go b/pkg/lib/authn/stdattrs/normalizer_mock_test.go index 95f9bf55ab..20a44156c2 100644 --- a/pkg/lib/authn/stdattrs/normalizer_mock_test.go +++ b/pkg/lib/authn/stdattrs/normalizer_mock_test.go @@ -7,8 +7,8 @@ package stdattrs import ( reflect "reflect" + internalinterface "github.com/authgear/authgear-server/pkg/api/internalinterface" model "github.com/authgear/authgear-server/pkg/api/model" - loginid "github.com/authgear/authgear-server/pkg/lib/authn/identity/loginid" gomock "github.com/golang/mock/gomock" ) @@ -36,10 +36,10 @@ func (m *MockLoginIDNormalizerFactory) EXPECT() *MockLoginIDNormalizerFactoryMoc } // NormalizerWithLoginIDType mocks base method. -func (m *MockLoginIDNormalizerFactory) NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) loginid.Normalizer { +func (m *MockLoginIDNormalizerFactory) NormalizerWithLoginIDType(loginIDKeyType model.LoginIDKeyType) internalinterface.LoginIDNormalizer { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NormalizerWithLoginIDType", loginIDKeyType) - ret0, _ := ret[0].(loginid.Normalizer) + ret0, _ := ret[0].(internalinterface.LoginIDNormalizer) return ret0 } From 5d13b08d7dcf6d5a631c0d8b08f4e51df1fd35be Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Tue, 14 May 2024 14:33:05 +0800 Subject: [PATCH 30/30] Fix oauth provider not registered in e2e executable --- e2e/cmd/e2e/main.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/e2e/cmd/e2e/main.go b/e2e/cmd/e2e/main.go index 0a5f210f29..df7ee5a12a 100644 --- a/e2e/cmd/e2e/main.go +++ b/e2e/cmd/e2e/main.go @@ -9,6 +9,15 @@ import ( _ "go.uber.org/automaxprocs" cmd "github.com/authgear/authgear-server/e2e/cmd/e2e/cmd" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/adfs" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/apple" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadb2c" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/azureadv2" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/facebook" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/github" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/google" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/linkedin" + _ "github.com/authgear/authgear-server/pkg/lib/oauthrelyingparty/wechat" "github.com/authgear/authgear-server/pkg/util/debug" )