package oauth import ( "context" "errors" "strings" "golang.org/x/oauth2" "base/config" "base/internal/pkg/oauth/github" "base/internal/pkg/oauth/google" "base/internal/pkg/oauth/linkedin" "base/internal/pkg/oauth/mock" "base/internal/pkg/oauth/types" ) // Token is an alias for types.Token for backward compatibility type Token = types.Token type OAuth struct { google types.Oauth linkedin types.Oauth github types.Oauth mock types.Oauth } type Config struct { GoogleConfig oauth2.Config GitHubConfig oauth2.Config LinkedinConfig oauth2.Config } func New(cfg *config.AppConfig) OAuth { oauthConfig := Config{ GoogleConfig: oauth2.Config{ ClientID: cfg.OAuth.Google.ClientID, ClientSecret: cfg.OAuth.Google.ClientSecret, RedirectURL: cfg.OAuth.Google.RedirectURL, Scopes: cfg.OAuth.Google.Scopes, }, GitHubConfig: oauth2.Config{ ClientID: cfg.OAuth.GitHub.ClientID, ClientSecret: cfg.OAuth.GitHub.ClientSecret, RedirectURL: cfg.OAuth.GitHub.RedirectURL, Scopes: cfg.OAuth.GitHub.Scopes, }, LinkedinConfig: oauth2.Config{ ClientID: cfg.OAuth.LinkedIn.ClientID, ClientSecret: cfg.OAuth.LinkedIn.ClientSecret, RedirectURL: cfg.OAuth.LinkedIn.RedirectURL, Scopes: cfg.OAuth.LinkedIn.Scopes, }, } o := OAuth{ google: google.New(oauthConfig.GoogleConfig), linkedin: linkedin.New(oauthConfig.LinkedinConfig), github: github.New(oauthConfig.GitHubConfig), } if cfg.OAuth.Mock.Enabled && strings.TrimSpace(cfg.OAuth.Mock.BaseURL) != "" { baseURL := strings.TrimSuffix(strings.TrimSpace(cfg.OAuth.Mock.BaseURL), "/") mockConfig := oauth2.Config{ ClientID: cfg.OAuth.Mock.ClientID, ClientSecret: cfg.OAuth.Mock.ClientSecret, RedirectURL: cfg.OAuth.Mock.RedirectURL, Scopes: cfg.OAuth.Mock.Scopes, } if mockConfig.ClientID == "" { mockConfig.ClientID = "mock-client" } if mockConfig.ClientSecret == "" { mockConfig.ClientSecret = "mock-secret" } if mockConfig.RedirectURL == "" { mockConfig.RedirectURL = "http://localhost:3000/auth/callback" } o.mock = mock.New(mockConfig, baseURL) } return o } func (a OAuth) Client(provider Provider) types.Oauth { switch provider { case Google: return a.google case Linkedin: return a.linkedin case GitHub: return a.github case Mock: if a.mock != nil { return a.mock } return disabledMockClient{} default: return a.google } } // ErrMockNotEnabled is returned when mock provider is used but not configured var ErrMockNotEnabled = errors.New("oauth mock is not enabled - set oauth.mock.enabled=true and oauth.mock.base_url") // disabledMockClient is used when mock is requested but not configured type disabledMockClient struct{} func (disabledMockClient) GetConsentAuthUrl(_ context.Context, _ string) string { panic("oauth mock is not enabled - set oauth.mock.enabled=true and oauth.mock.base_url") } func (disabledMockClient) ExchangeCodeWithToken(context.Context, string) (*types.Token, error) { return nil, ErrMockNotEnabled } func (disabledMockClient) GetUserInfo(context.Context, string, string) (types.UserInfo, error) { return nil, ErrMockNotEnabled }