@@ -0,0 +1,108 @@
1+package providers
2+
3+import (
4+ "bytes"
5+ "context"
6+ "fmt"
7+ "net/url"
8+
9+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
10+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
11+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
12+)
13+
14+type SourceHutProvider struct {
15+ *ProviderData
16+}
17+
18+var _ Provider = (*SourceHutProvider)(nil)
19+
20+const (
21+ SourceHutProviderName = "SourceHut"
22+ SourceHutDefaultScope = "meta.sr.ht/PROFILE:RO"
23+)
24+
25+var (
26+ // Default Login URL for SourceHut.
27+ // Pre-parsed URL of https://meta.sr.ht/oauth2/authorize.
28+ SourceHutDefaultLoginURL = &url.URL{
29+ Scheme: "https",
30+ Host: "meta.sr.ht",
31+ Path: "/oauth2/authorize",
32+ }
33+
34+ // Default Redeem URL for SourceHut.
35+ // Pre-parsed URL of https://meta.sr.ht/oauth2/access-token.
36+ SourceHutDefaultRedeemURL = &url.URL{
37+ Scheme: "https",
38+ Host: "meta.sr.ht",
39+ Path: "/oauth2/access-token",
40+ }
41+
42+ // Default Profile URL for SourceHut.
43+ // Pre-parsed URL of https://meta.sr.ht/query.
44+ SourceHutDefaultProfileURL = &url.URL{
45+ Scheme: "https",
46+ Host: "meta.sr.ht",
47+ Path: "/query",
48+ }
49+
50+ // Default Validation URL for SourceHut.
51+ // Pre-parsed URL of https://meta.sr.ht/profile.
52+ SourceHutDefaultValidateURL = &url.URL{
53+ Scheme: "https",
54+ Host: "meta.sr.ht",
55+ Path: "/profile",
56+ }
57+)
58+
59+// NewSourceHutProvider creates a SourceHutProvider using the passed ProviderData
60+func NewSourceHutProvider(p *ProviderData) *SourceHutProvider {
61+ p.setProviderDefaults(providerDefaults{
62+ name: SourceHutProviderName,
63+ loginURL: SourceHutDefaultLoginURL,
64+ redeemURL: SourceHutDefaultRedeemURL,
65+ profileURL: SourceHutDefaultProfileURL,
66+ validateURL: SourceHutDefaultValidateURL,
67+ scope: SourceHutDefaultScope,
68+ })
69+
70+ return &SourceHutProvider{ProviderData: p}
71+}
72+
73+// EnrichSession uses the SourceHut userinfo endpoint to populate the session's
74+// email and username.
75+func (p *SourceHutProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
76+ json, err := requests.New(p.ProfileURL.String()).
77+ WithContext(ctx).
78+ WithMethod("POST").
79+ SetHeader("Content-Type", "application/json").
80+ SetHeader("Authorization", "Bearer "+s.AccessToken).
81+ WithBody(bytes.NewBufferString(`{"query": "{ me { username, email } }"}`)).
82+ Do().
83+ UnmarshalSimpleJSON()
84+ if err != nil {
85+ logger.Errorf("failed making request %v", err)
86+ return err
87+ }
88+
89+ email, err := json.GetPath("data", "me", "email").String()
90+ if err != nil {
91+ return fmt.Errorf("unable to extract email from userinfo endpoint: %v", err)
92+ }
93+ s.Email = email
94+
95+ username, err := json.GetPath("data", "me", "username").String()
96+ if err != nil {
97+ return fmt.Errorf("unable to extract username from userinfo endpoint: %v", err)
98+ }
99+ s.PreferredUsername = username
100+ s.User = username
101+
102+ return nil
103+}
104+
105+// ValidateSession validates the AccessToken
106+func (p *SourceHutProvider) ValidateSession(ctx context.Context, s *sessions.SessionState) bool {
107+ return validateToken(ctx, p, s.AccessToken, makeOIDCHeader(s.AccessToken))
108+}