package service import ( "context" "crypto/tls" "net/http" "time" "github.com/steveiliop56/tinyauth/internal/config" "golang.org/x/oauth2" ) type UserinfoExtractor func(client *http.Client, url string) (config.Claims, error) type OAuthService struct { serviceCfg config.OAuthServiceConfig config *oauth2.Config ctx context.Context userinfoExtractor UserinfoExtractor } func NewOAuthService(config config.OAuthServiceConfig) *OAuthService { httpClient := &http.Client{ Timeout: 30 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: config.Insecure, }, }, } ctx := context.Background() ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient) return &OAuthService{ serviceCfg: config, config: &oauth2.Config{ ClientID: config.ClientID, ClientSecret: config.ClientSecret, RedirectURL: config.RedirectURL, Scopes: config.Scopes, Endpoint: oauth2.Endpoint{ AuthURL: config.AuthURL, TokenURL: config.TokenURL, }, }, ctx: ctx, userinfoExtractor: defaultExtractor, } } func (s *OAuthService) WithUserinfoExtractor(extractor UserinfoExtractor) *OAuthService { s.userinfoExtractor = extractor return s } func (s *OAuthService) Name() string { return s.serviceCfg.Name } func (s *OAuthService) NewRandom() string { // The generate verifier function just creates a random string, // so we can use it to generate a random state as well random := oauth2.GenerateVerifier() return random } func (s *OAuthService) GetAuthURL(state string, verifier string) string { return s.config.AuthCodeURL(state, oauth2.AccessTypeOnline, oauth2.S256ChallengeOption(verifier)) } func (s *OAuthService) GetToken(code string, verifier string) (*oauth2.Token, error) { return s.config.Exchange(s.ctx, code, oauth2.VerifierOption(verifier)) } func (s *OAuthService) GetUserinfo(token *oauth2.Token) (config.Claims, error) { client := oauth2.NewClient(s.ctx, oauth2.StaticTokenSource(token)) return s.userinfoExtractor(client, s.serviceCfg.UserinfoURL) }