Skip to main content
Glama

Storyden

by Southclaws
Mozilla Public License 2.0
229
google.go4.37 kB
package google import ( "context" "fmt" "net/mail" "strings" "time" "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" "github.com/Southclaws/fault/fmsg" "github.com/Southclaws/fault/ftag" petname "github.com/dustinkirkland/golang-petname" "github.com/gosimple/slug" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ga "google.golang.org/api/oauth2/v2" "google.golang.org/api/option" "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/account/authentication" "github.com/Southclaws/storyden/app/services/account/register" "github.com/Southclaws/storyden/app/services/authentication/provider/oauth" "github.com/Southclaws/storyden/internal/config" "github.com/Southclaws/storyden/internal/infrastructure/endec" ) var ( service = authentication.ServiceOAuthGoogle tokenType = authentication.TokenTypeOAuth ) type Provider struct { config oauth.Configuration register *register.Registrar ed endec.EncrypterDecrypter } func New( cfg config.Config, register *register.Registrar, ed endec.EncrypterDecrypter, ) (*Provider, error) { if cfg.GoogleEnabled && ed == nil { return nil, fault.New("JWT provider must be enabled by setting JWT_SECRET for Google OAuth provider") } return &Provider{ config: oauth.Configuration{ Enabled: cfg.GoogleEnabled, ClientID: cfg.GoogleClientID, ClientSecret: cfg.GoogleClientSecret, }, register: register, ed: ed, }, nil } func (p *Provider) Service() authentication.Service { return service } func (p *Provider) Token() authentication.TokenType { return tokenType } func (p *Provider) Enabled(ctx context.Context) (bool, error) { return p.config.Enabled, nil } func (p *Provider) oauthConfig(redirect string) *oauth2.Config { return &oauth2.Config{ ClientID: p.config.ClientID, ClientSecret: p.config.ClientSecret, Endpoint: google.Endpoint, RedirectURL: redirect, Scopes: []string{ "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", }, } } func (p *Provider) Link(redirectPath string) (string, error) { state, err := p.ed.Encrypt(map[string]any{ "redirect": redirectPath, }, time.Minute*10) if err != nil { return "", fault.Wrap(err) } oac := p.oauthConfig(redirectPath) return oac.AuthCodeURL(state, oauth2.AccessTypeOffline), nil } func (p *Provider) Login(ctx context.Context, state, code string) (*account.Account, error) { c, err := p.ed.Decrypt(state) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), fmsg.WithDesc("failed to decrypt state value", "This link has expired, please try again."), ) } oac := p.oauthConfig(c["redirect"].(string)) token, err := oac.Exchange(ctx, code, oauth2.AccessTypeOffline) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument), fmsg.WithDesc("failed to exchange code for token", "This login token may have expired, please try again."), ) } gs, err := ga.NewService(ctx, option.WithTokenSource(oauth2.StaticTokenSource(token))) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), fmsg.WithDesc("failed to create Google API service", "Unable to connect to Google. Please try again.")) } u, err := ga.NewUserinfoV2MeService(gs).Get().Do() if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), fmsg.WithDesc("failed to fetch Google user info", "Unable to retrieve your Google profile. Please try again.")) } handle := slug.Make(fmt.Sprintf("%s %s", u.GivenName, petname.Generate(2, "-"))) name := fmt.Sprint(u.GivenName, " ", u.FamilyName) email, err := mail.ParseAddress(u.Email) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), fmsg.WithDesc("failed to parse Google email address", "The email address from Google is invalid. Please check your Google account settings.")) } authName := fmt.Sprintf("Google (%s)", email.Address) acc, err := p.register.GetOrCreateViaEmail(ctx, service, authName, strings.ToLower(u.Id), token.AccessToken, handle, name, *email, ) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), fmsg.WithDesc("failed to create or retrieve account", "Unable to complete sign-in. Please try again or contact site administration.")) } return acc, nil }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Southclaws/storyden'

If you have feedback or need assistance with the MCP directory API, please join our Discord server