212 lines
5.2 kB
1
package auth
2
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
"time"
8
9
comatproto "github.com/bluesky-social/indigo/api/atproto"
10
"github.com/bluesky-social/indigo/atproto/identity"
11
"github.com/bluesky-social/indigo/xrpc"
12
"github.com/gorilla/sessions"
13
"github.com/sotangled/tangled/appview"
14
)
15
16
type Auth struct {
17
Store *sessions.CookieStore
18
}
19
20
type AtSessionCreate struct {
21
comatproto.ServerCreateSession_Output
22
PDSEndpoint string
23
}
24
25
type AtSessionRefresh struct {
26
comatproto.ServerRefreshSession_Output
27
PDSEndpoint string
28
}
29
30
func Make(secret string) (*Auth, error) {
31
store := sessions.NewCookieStore([]byte(secret))
32
return &Auth{store}, nil
33
}
34
35
func (a *Auth) CreateInitialSession(ctx context.Context, resolved *identity.Identity, appPassword string) (*comatproto.ServerCreateSession_Output, error) {
36
37
pdsUrl := resolved.PDSEndpoint()
38
client := xrpc.Client{
39
Host: pdsUrl,
40
}
41
42
atSession, err := comatproto.ServerCreateSession(ctx, &client, &comatproto.ServerCreateSession_Input{
43
Identifier: resolved.DID.String(),
44
Password: appPassword,
45
})
46
if err != nil {
47
return nil, fmt.Errorf("invalid app password")
48
}
49
50
return atSession, nil
51
}
52
53
// Sessionish is an interface that provides access to the common fields of both types.
54
type Sessionish interface {
55
GetAccessJwt() string
56
GetActive() *bool
57
GetDid() string
58
GetDidDoc() *interface{}
59
GetHandle() string
60
GetRefreshJwt() string
61
GetStatus() *string
62
}
63
64
// Create a wrapper type for ServerRefreshSession_Output
65
type RefreshSessionWrapper struct {
66
*comatproto.ServerRefreshSession_Output
67
}
68
69
func (s *RefreshSessionWrapper) GetAccessJwt() string {
70
return s.AccessJwt
71
}
72
73
func (s *RefreshSessionWrapper) GetActive() *bool {
74
return s.Active
75
}
76
77
func (s *RefreshSessionWrapper) GetDid() string {
78
return s.Did
79
}
80
81
func (s *RefreshSessionWrapper) GetDidDoc() *interface{} {
82
return s.DidDoc
83
}
84
85
func (s *RefreshSessionWrapper) GetHandle() string {
86
return s.Handle
87
}
88
89
func (s *RefreshSessionWrapper) GetRefreshJwt() string {
90
return s.RefreshJwt
91
}
92
93
func (s *RefreshSessionWrapper) GetStatus() *string {
94
return s.Status
95
}
96
97
// Create a wrapper type for ServerRefreshSession_Output
98
type CreateSessionWrapper struct {
99
*comatproto.ServerCreateSession_Output
100
}
101
102
func (s *CreateSessionWrapper) GetAccessJwt() string {
103
return s.AccessJwt
104
}
105
106
func (s *CreateSessionWrapper) GetActive() *bool {
107
return s.Active
108
}
109
110
func (s *CreateSessionWrapper) GetDid() string {
111
return s.Did
112
}
113
114
func (s *CreateSessionWrapper) GetDidDoc() *interface{} {
115
return s.DidDoc
116
}
117
118
func (s *CreateSessionWrapper) GetHandle() string {
119
return s.Handle
120
}
121
122
func (s *CreateSessionWrapper) GetRefreshJwt() string {
123
return s.RefreshJwt
124
}
125
126
func (s *CreateSessionWrapper) GetStatus() *string {
127
return s.Status
128
}
129
130
func (a *Auth) ClearSession(r *http.Request, w http.ResponseWriter) error {
131
clientSession, _ := a.Store.Get(r, appview.SessionName)
132
clientSession.Options.MaxAge = -1
133
return clientSession.Save(r, w)
134
}
135
136
func (a *Auth) StoreSession(r *http.Request, w http.ResponseWriter, atSessionish Sessionish, pdsEndpoint string) error {
137
clientSession, _ := a.Store.Get(r, appview.SessionName)
138
clientSession.Values[appview.SessionHandle] = atSessionish.GetHandle()
139
clientSession.Values[appview.SessionDid] = atSessionish.GetDid()
140
clientSession.Values[appview.SessionPds] = pdsEndpoint
141
clientSession.Values[appview.SessionAccessJwt] = atSessionish.GetAccessJwt()
142
clientSession.Values[appview.SessionRefreshJwt] = atSessionish.GetRefreshJwt()
143
clientSession.Values[appview.SessionExpiry] = time.Now().Add(time.Minute * 15).Format(time.RFC3339)
144
clientSession.Values[appview.SessionAuthenticated] = true
145
return clientSession.Save(r, w)
146
}
147
148
func (a *Auth) AuthorizedClient(r *http.Request) (*xrpc.Client, error) {
149
clientSession, err := a.Store.Get(r, "appview-session")
150
151
if err != nil || clientSession.IsNew {
152
return nil, err
153
}
154
155
did := clientSession.Values["did"].(string)
156
pdsUrl := clientSession.Values["pds"].(string)
157
accessJwt := clientSession.Values["accessJwt"].(string)
158
refreshJwt := clientSession.Values["refreshJwt"].(string)
159
160
client := &xrpc.Client{
161
Host: pdsUrl,
162
Auth: &xrpc.AuthInfo{
163
AccessJwt: accessJwt,
164
RefreshJwt: refreshJwt,
165
Did: did,
166
},
167
}
168
169
return client, nil
170
}
171
172
func (a *Auth) GetSession(r *http.Request) (*sessions.Session, error) {
173
return a.Store.Get(r, appview.SessionName)
174
}
175
176
func (a *Auth) GetDid(r *http.Request) string {
177
clientSession, err := a.Store.Get(r, appview.SessionName)
178
if err != nil || clientSession.IsNew {
179
return ""
180
}
181
182
return clientSession.Values[appview.SessionDid].(string)
183
}
184
185
func (a *Auth) GetHandle(r *http.Request) string {
186
clientSession, err := a.Store.Get(r, appview.SessionName)
187
if err != nil || clientSession.IsNew {
188
return ""
189
}
190
191
return clientSession.Values[appview.SessionHandle].(string)
192
}
193
194
type User struct {
195
Handle string
196
Did string
197
Pds string
198
}
199
200
func (a *Auth) GetUser(r *http.Request) *User {
201
clientSession, err := a.Store.Get(r, appview.SessionName)
202
203
if err != nil || clientSession.IsNew {
204
return nil
205
}
206
207
return &User{
208
Handle: clientSession.Values[appview.SessionHandle].(string),
209
Did: clientSession.Values[appview.SessionDid].(string),
210
Pds: clientSession.Values[appview.SessionPds].(string),
211
}
212
}
213