everything you need to create an atproto appview
1package oauth
2
3import (
4 "fmt"
5 "net"
6 "net/http"
7 "time"
8
9 "github.com/bluesky-social/indigo/atproto/auth/oauth"
10 atpclient "github.com/bluesky-social/indigo/atproto/client"
11 atcrypto "github.com/bluesky-social/indigo/atproto/crypto"
12 "github.com/charmbracelet/log"
13 "github.com/gorilla/sessions"
14 "tangled.org/oppi.li/atproto-starterkit/appview/config"
15 "tangled.org/oppi.li/atproto-starterkit/idresolver"
16)
17
18type OAuth struct {
19 ClientApp *oauth.ClientApp
20 SessStore *sessions.CookieStore
21 Config *config.Config
22 IdResolver *idresolver.Resolver
23 Logger *log.Logger
24}
25
26func New(config *config.Config, res *idresolver.Resolver, logger *log.Logger) (*OAuth, error) {
27 var oauthConfig oauth.ClientConfig
28 var clientUri string
29
30 if config.Core.Dev {
31 clientUri = "http://" + net.JoinHostPort("127.0.0.1", config.Core.Port())
32 callbackUri := clientUri + "/oauth/callback"
33 oauthConfig = oauth.NewLocalhostConfig(callbackUri, Scopes)
34 } else {
35 clientUri = config.Core.Host
36 clientId := fmt.Sprintf("%s/oauth/client-metadata.json", clientUri)
37 callbackUri := clientUri + "/oauth/callback"
38 oauthConfig = oauth.NewPublicConfig(clientId, callbackUri, Scopes)
39 }
40
41 // configure client secret
42 priv, err := atcrypto.ParsePrivateMultibase(config.OAuth.ClientSecret)
43 if err != nil {
44 return nil, err
45 }
46 if err := oauthConfig.SetClientSecret(priv, config.OAuth.ClientKid); err != nil {
47 return nil, err
48 }
49
50 authStore, err := NewRedisStore(&RedisStoreConfig{
51 RedisURL: config.Redis.ToURL(),
52 SessionExpiryDuration: time.Hour * 24 * 90,
53 SessionInactivityDuration: time.Hour * 24 * 14,
54 AuthRequestExpiryDuration: time.Minute * 30,
55 })
56 if err != nil {
57 return nil, err
58 }
59
60 sessStore := sessions.NewCookieStore([]byte(config.Core.CookieSecret))
61
62 clientApp := oauth.NewClientApp(&oauthConfig, authStore)
63 clientApp.Dir = res.Directory()
64
65 // allow non-public transports in dev mode
66 if config.Core.Dev {
67 clientApp.Resolver.Client.Transport = http.DefaultTransport
68 }
69
70 logger.Info("oauth setup successfully", "confidential", clientApp.Config.IsConfidential())
71 return &OAuth{
72 ClientApp: clientApp,
73 Config: config,
74 SessStore: sessStore,
75 IdResolver: res,
76 Logger: logger,
77 }, nil
78}
79
80func (o *OAuth) AuthorizedClient(r *http.Request) (*atpclient.APIClient, error) {
81 session, err := o.ResumeSession(r)
82 if err != nil {
83 return nil, fmt.Errorf("error getting session: %w", err)
84 }
85 return session.APIClient(), nil
86}