all repos — homestead @ 35a67d739dd5cd5cf71e5f37762808568272ede5

Code for my website

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)
}