A vibe coded tangled fork which supports pijul.
1package oauth
2
3import (
4 "encoding/json"
5 "errors"
6 "net/http"
7 "time"
8)
9
10const MaxAccounts = 20
11
12var ErrMaxAccountsReached = errors.New("maximum number of linked accounts reached")
13
14type AccountInfo struct {
15 Did string `json:"did"`
16 Handle string `json:"handle"`
17 SessionId string `json:"session_id"`
18 AddedAt int64 `json:"added_at"`
19}
20
21type AccountRegistry struct {
22 Accounts []AccountInfo `json:"accounts"`
23}
24
25type MultiAccountUser struct {
26 Active *User
27 Accounts []AccountInfo
28}
29
30func (m *MultiAccountUser) Did() string {
31 if m.Active == nil {
32 return ""
33 }
34 return m.Active.Did
35}
36
37func (o *OAuth) GetAccounts(r *http.Request) *AccountRegistry {
38 session, err := o.SessStore.Get(r, AccountsName)
39 if err != nil || session.IsNew {
40 return &AccountRegistry{Accounts: []AccountInfo{}}
41 }
42
43 data, ok := session.Values["accounts"].(string)
44 if !ok {
45 return &AccountRegistry{Accounts: []AccountInfo{}}
46 }
47
48 var registry AccountRegistry
49 if err := json.Unmarshal([]byte(data), ®istry); err != nil {
50 return &AccountRegistry{Accounts: []AccountInfo{}}
51 }
52
53 return ®istry
54}
55
56func (o *OAuth) saveAccounts(w http.ResponseWriter, r *http.Request, registry *AccountRegistry) error {
57 session, err := o.SessStore.Get(r, AccountsName)
58 if err != nil {
59 o.Logger.Warn("failed to decode existing accounts cookie, will create new", "err", err)
60 }
61
62 data, err := json.Marshal(registry)
63 if err != nil {
64 return err
65 }
66
67 session.Values["accounts"] = string(data)
68 session.Options.MaxAge = 60 * 60 * 24 * 365
69 session.Options.HttpOnly = true
70 session.Options.Secure = !o.Config.Core.Dev
71 session.Options.SameSite = http.SameSiteLaxMode
72
73 return session.Save(r, w)
74}
75
76func (r *AccountRegistry) AddAccount(did, handle, sessionId string) error {
77 for i, acc := range r.Accounts {
78 if acc.Did == did {
79 r.Accounts[i].SessionId = sessionId
80 r.Accounts[i].Handle = handle
81 return nil
82 }
83 }
84
85 if len(r.Accounts) >= MaxAccounts {
86 return ErrMaxAccountsReached
87 }
88
89 r.Accounts = append(r.Accounts, AccountInfo{
90 Did: did,
91 Handle: handle,
92 SessionId: sessionId,
93 AddedAt: time.Now().Unix(),
94 })
95 return nil
96}
97
98func (r *AccountRegistry) RemoveAccount(did string) {
99 filtered := make([]AccountInfo, 0, len(r.Accounts))
100 for _, acc := range r.Accounts {
101 if acc.Did != did {
102 filtered = append(filtered, acc)
103 }
104 }
105 r.Accounts = filtered
106}
107
108func (r *AccountRegistry) FindAccount(did string) *AccountInfo {
109 for i := range r.Accounts {
110 if r.Accounts[i].Did == did {
111 return &r.Accounts[i]
112 }
113 }
114 return nil
115}
116
117func (o *OAuth) GetMultiAccountUser(r *http.Request) *MultiAccountUser {
118 sess, err := o.ResumeSession(r)
119 if err != nil {
120 return nil
121 }
122
123 registry := o.GetAccounts(r)
124 return &MultiAccountUser{
125 Active: &User{
126 Did: sess.Data.AccountDID.String(),
127 },
128 Accounts: registry.Accounts,
129 }
130}
131
132type AuthReturnInfo struct {
133 ReturnURL string
134 AddAccount bool
135}
136
137func (o *OAuth) SetAuthReturn(w http.ResponseWriter, r *http.Request, returnURL string, addAccount bool) error {
138 session, err := o.SessStore.Get(r, AuthReturnName)
139 if err != nil {
140 return err
141 }
142
143 session.Values[AuthReturnURL] = returnURL
144 session.Values[AuthAddAccount] = addAccount
145 session.Options.MaxAge = 60 * 30
146 session.Options.HttpOnly = true
147 session.Options.Secure = !o.Config.Core.Dev
148 session.Options.SameSite = http.SameSiteLaxMode
149
150 return session.Save(r, w)
151}
152
153func (o *OAuth) GetAuthReturn(r *http.Request) *AuthReturnInfo {
154 session, err := o.SessStore.Get(r, AuthReturnName)
155 if err != nil || session.IsNew {
156 return &AuthReturnInfo{}
157 }
158
159 returnURL, _ := session.Values[AuthReturnURL].(string)
160 addAccount, _ := session.Values[AuthAddAccount].(bool)
161
162 return &AuthReturnInfo{
163 ReturnURL: returnURL,
164 AddAccount: addAccount,
165 }
166}
167
168func (o *OAuth) ClearAuthReturn(w http.ResponseWriter, r *http.Request) error {
169 session, err := o.SessStore.Get(r, AuthReturnName)
170 if err != nil {
171 return err
172 }
173
174 session.Options.MaxAge = -1
175 return session.Save(r, w)
176}