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