A vibe coded tangled fork which supports pijul.
at 313036db827bee64dc4940a0cb73d5c7dcf1ce46 184 lines 4.2 kB view raw
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), &registry); err != nil { 50 return &AccountRegistry{Accounts: []AccountInfo{}} 51 } 52 53 return &registry 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 (r *AccountRegistry) OtherAccounts(activeDid string) []AccountInfo { 118 result := make([]AccountInfo, 0, len(r.Accounts)) 119 for _, acc := range r.Accounts { 120 if acc.Did != activeDid { 121 result = append(result, acc) 122 } 123 } 124 return result 125} 126 127func (o *OAuth) GetMultiAccountUser(r *http.Request) *MultiAccountUser { 128 user := o.GetUser(r) 129 if user == nil { 130 return nil 131 } 132 133 registry := o.GetAccounts(r) 134 return &MultiAccountUser{ 135 Active: user, 136 Accounts: registry.Accounts, 137 } 138} 139 140type AuthReturnInfo struct { 141 ReturnURL string 142 AddAccount bool 143} 144 145func (o *OAuth) SetAuthReturn(w http.ResponseWriter, r *http.Request, returnURL string, addAccount bool) error { 146 session, err := o.SessStore.Get(r, AuthReturnName) 147 if err != nil { 148 return err 149 } 150 151 session.Values[AuthReturnURL] = returnURL 152 session.Values[AuthAddAccount] = addAccount 153 session.Options.MaxAge = 60 * 30 154 session.Options.HttpOnly = true 155 session.Options.Secure = !o.Config.Core.Dev 156 session.Options.SameSite = http.SameSiteLaxMode 157 158 return session.Save(r, w) 159} 160 161func (o *OAuth) GetAuthReturn(r *http.Request) *AuthReturnInfo { 162 session, err := o.SessStore.Get(r, AuthReturnName) 163 if err != nil || session.IsNew { 164 return &AuthReturnInfo{} 165 } 166 167 returnURL, _ := session.Values[AuthReturnURL].(string) 168 addAccount, _ := session.Values[AuthAddAccount].(bool) 169 170 return &AuthReturnInfo{ 171 ReturnURL: returnURL, 172 AddAccount: addAccount, 173 } 174} 175 176func (o *OAuth) ClearAuthReturn(w http.ResponseWriter, r *http.Request) error { 177 session, err := o.SessStore.Get(r, AuthReturnName) 178 if err != nil { 179 return err 180 } 181 182 session.Options.MaxAge = -1 183 return session.Save(r, w) 184}