package oauth import ( "encoding/json" "errors" "net/http" "time" ) const MaxAccounts = 20 var ErrMaxAccountsReached = errors.New("maximum number of linked accounts reached") type AccountInfo struct { Did string `json:"did"` SessionId string `json:"session_id"` AddedAt int64 `json:"added_at"` } type AccountRegistry struct { Accounts []AccountInfo `json:"accounts"` } type MultiAccountUser struct { Active User Accounts []AccountInfo } func (m *MultiAccountUser) Did() string { return m.Active.Did } func (o *OAuth) GetAccounts(r *http.Request) *AccountRegistry { session, err := o.SessStore.Get(r, AccountsName) if err != nil || session.IsNew { return &AccountRegistry{Accounts: []AccountInfo{}} } data, ok := session.Values["accounts"].(string) if !ok { return &AccountRegistry{Accounts: []AccountInfo{}} } var registry AccountRegistry if err := json.Unmarshal([]byte(data), ®istry); err != nil { return &AccountRegistry{Accounts: []AccountInfo{}} } return ®istry } func (o *OAuth) saveAccounts(w http.ResponseWriter, r *http.Request, registry *AccountRegistry) error { session, err := o.SessStore.Get(r, AccountsName) if err != nil { return err } data, err := json.Marshal(registry) if err != nil { return err } session.Values["accounts"] = string(data) session.Options.MaxAge = 60 * 60 * 24 * 365 session.Options.HttpOnly = true session.Options.Secure = !o.Config.Core.Dev session.Options.SameSite = http.SameSiteLaxMode return session.Save(r, w) } func (r *AccountRegistry) AddAccount(did, handle, sessionId string) error { for i, acc := range r.Accounts { if acc.Did == did { r.Accounts[i].SessionId = sessionId return nil } } if len(r.Accounts) >= MaxAccounts { return ErrMaxAccountsReached } r.Accounts = append(r.Accounts, AccountInfo{ Did: did, SessionId: sessionId, AddedAt: time.Now().Unix(), }) return nil } func (r *AccountRegistry) RemoveAccount(did string) { filtered := make([]AccountInfo, 0, len(r.Accounts)) for _, acc := range r.Accounts { if acc.Did != did { filtered = append(filtered, acc) } } r.Accounts = filtered } func (r *AccountRegistry) FindAccount(did string) *AccountInfo { for i := range r.Accounts { if r.Accounts[i].Did == did { return &r.Accounts[i] } } return nil } func (o *OAuth) GetMultiAccountUser(r *http.Request) *MultiAccountUser { sess, err := o.ResumeSession(r) if err != nil { return nil } registry := o.GetAccounts(r) return &MultiAccountUser{ Active: User{ Did: sess.Data.AccountDID.String(), }, Accounts: registry.Accounts, } } func (o *OAuth) SetAuthReturn(w http.ResponseWriter, r *http.Request, returnURL string) error { session, err := o.SessStore.Get(r, AuthReturnName) if err != nil { return err } session.Values[AuthReturnURL] = returnURL session.Options.MaxAge = 60 * 30 session.Options.HttpOnly = true session.Options.Secure = !o.Config.Core.Dev session.Options.SameSite = http.SameSiteLaxMode return session.Save(r, w) } func (o *OAuth) GetAuthReturn(r *http.Request) string { session, err := o.SessStore.Get(r, AuthReturnName) if err != nil || session.IsNew { return "" } returnURL, _ := session.Values[AuthReturnURL].(string) return returnURL } func (o *OAuth) ClearAuthReturn(w http.ResponseWriter, r *http.Request) error { session, err := o.SessStore.Get(r, AuthReturnName) if err != nil { return err } session.Options.MaxAge = -1 return session.Save(r, w) }