domain/content/publisher/app.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 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)
}
|