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