A vibe coded tangled fork which supports pijul.
at 88d05784f6373375f740685ea63a412eb32c2782 176 lines 4.0 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 (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}