Monorepo for Tangled
at master 191 lines 4.3 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 (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), &registry); err != nil { 57 return &AccountRegistry{Accounts: []AccountInfo{}} 58 } 59 60 return &registry 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 o.Logger.Warn("failed to decode existing accounts cookie, will create new", "err", 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}