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