package publisher import ( "context" "net/http" "alin.ovh/x/log" "github.com/Southclaws/fault" "github.com/Southclaws/fault/fmsg" "github.com/coreos/go-oidc/v3/oidc" "go.hacdias.com/indielib/indieauth" "golang.org/x/oauth2" "alin.ovh/homestead/domain/web/templates" "alin.ovh/homestead/shared/config" ihttp "alin.ovh/homestead/shared/http" ) type OIDCClientConfig struct { URL config.URL ClientID string ClientSecret string } type Options struct { Development bool `conf:"-"` OIDC OIDCClientConfig BaseURL *config.URL VCSRemoteURL *config.URL `conf:"default:https://git.alin.ovh/website"` } type Service struct { log *log.Logger indieauthServer *indieauth.Server siteSettings templates.SiteSettings baseURL *config.URL oauth2Config oauth2.Config oidcVerifier *oidc.IDTokenVerifier } func New(opts *Options, log *log.Logger) (*Service, error) { ctx := context.Background() provider, err := oidc.NewProvider(ctx, opts.OIDC.URL.String()) if err != nil { return nil, fault.Wrap(err, fmsg.With("failed to create OIDC provider")) } if opts.OIDC.ClientID == "" || opts.OIDC.ClientSecret == "" { return nil, fault.New("OIDC client ID and secret are required") } oauth2Config := oauth2.Config{ ClientID: opts.OIDC.ClientID, ClientSecret: opts.OIDC.ClientSecret, RedirectURL: opts.BaseURL.JoinPath("/auth/callback").String(), // Discovery returns the OAuth2 endpoints. Endpoint: provider.Endpoint(), // "openid" is a required scope for OpenID Connect flows. Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } oidcConfig := &oidc.Config{ ClientID: opts.OIDC.ClientID, } verifier := provider.Verifier(oidcConfig) service := &Service{ log: log, baseURL: opts.BaseURL, indieauthServer: indieauth.NewServer(true, &http.Client{}), siteSettings: templates.SiteSettings{ Title: "Barkeep", Language: "en-GB", Menu: []config.MenuItem{}, InjectLiveReload: opts.Development, }, oauth2Config: oauth2Config, oidcVerifier: verifier, } if opts.BaseURL.Path == "" { opts.BaseURL.Path = "/" } err = indieauth.IsValidProfileURL(opts.BaseURL.String()) if err != nil { return nil, fault.Wrap(err, fmsg.With("invalid base URL")) } return service, nil } func (s *Service) RegisterHandlers(mux *ihttp.ServeMux) { mux.HandleFunc("/admin/style.css", s.Style) mux.HandleFunc("/admin/auth/login", s.Login) mux.HandleFunc("/admin/auth/callback", s.Callback) mux.HandleFunc("/admin/auth/logout", s.Logout) mux.HandleFunc("/admin/{$}", s.Index) }