mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-09 21:08:07 -06:00
[feature] overhaul the oidc system (#961)
* [feature] overhaul the oidc system this allows for more flexible username handling and prevents account takeover using old email addresses * [feature] add migration path for old OIDC users * [feature] nicer error reporting for users * [docs] document the new OIDC flow * [fix] return early on oidc error * [docs]: add comments on the finalization logic
This commit is contained in:
parent
1a3f26fb5c
commit
199b685f43
20 changed files with 335 additions and 119 deletions
|
|
@ -40,7 +40,7 @@ type Admin interface {
|
|||
|
||||
// NewSignup creates a new user in the database with the given parameters.
|
||||
// By the time this function is called, it should be assumed that all the parameters have passed validation!
|
||||
NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, Error)
|
||||
NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, externalID string, admin bool) (*gtsmodel.User, Error)
|
||||
|
||||
// CreateInstanceAccount creates an account in the database with the same username as the instance host value.
|
||||
// Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'.
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.
|
|||
return a.conn.NotExists(ctx, q)
|
||||
}
|
||||
|
||||
func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, db.Error) {
|
||||
func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, externalID string, admin bool) (*gtsmodel.User, db.Error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, rsaKeyBits)
|
||||
if err != nil {
|
||||
log.Errorf("error creating new rsa key: %s", err)
|
||||
|
|
@ -169,6 +169,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
|
|||
UnconfirmedEmail: email,
|
||||
CreatedByApplicationID: appID,
|
||||
Approved: &approved,
|
||||
ExternalID: externalID,
|
||||
}
|
||||
|
||||
if emailVerified {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func init() {
|
||||
up := func(ctx context.Context, db *bun.DB) error {
|
||||
_, err := db.ExecContext(ctx, "ALTER TABLE ? ADD COLUMN ? TEXT", bun.Ident("users"), bun.Ident("external_id"))
|
||||
if err != nil && !(strings.Contains(err.Error(), "already exists") || strings.Contains(err.Error(), "duplicate column name") || strings.Contains(err.Error(), "SQLSTATE 42701")) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
down := func(ctx context.Context, db *bun.DB) error {
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := Migrations.Register(up, down); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ func (u *userDB) init() {
|
|||
{Name: "AccountID"},
|
||||
{Name: "Email"},
|
||||
{Name: "ConfirmationToken"},
|
||||
{Name: "ExternalID"},
|
||||
}, func(u1 *gtsmodel.User) *gtsmodel.User {
|
||||
u2 := new(gtsmodel.User)
|
||||
*u2 = *u1
|
||||
|
|
@ -104,6 +105,24 @@ func (u *userDB) GetUserByEmailAddress(ctx context.Context, emailAddress string)
|
|||
return &user, nil
|
||||
}, emailAddress)
|
||||
}
|
||||
func (u *userDB) GetUserByExternalID(ctx context.Context, id string) (*gtsmodel.User, db.Error) {
|
||||
|
||||
return u.cache.Load("ExternalID", func() (*gtsmodel.User, error) {
|
||||
var user gtsmodel.User
|
||||
|
||||
q := u.conn.
|
||||
NewSelect().
|
||||
Model(&user).
|
||||
Relation("Account").
|
||||
Where("? = ?", bun.Ident("user.external_id"), id)
|
||||
|
||||
if err := q.Scan(ctx); err != nil {
|
||||
return nil, u.conn.ProcessError(err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}, id)
|
||||
}
|
||||
|
||||
func (u *userDB) GetUserByConfirmationToken(ctx context.Context, confirmationToken string) (*gtsmodel.User, db.Error) {
|
||||
return u.cache.Load("ConfirmationToken", func() (*gtsmodel.User, error) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ type User interface {
|
|||
GetUserByAccountID(ctx context.Context, accountID string) (*gtsmodel.User, Error)
|
||||
// GetUserByID returns one user with the given email address, or an error if something goes wrong.
|
||||
GetUserByEmailAddress(ctx context.Context, emailAddress string) (*gtsmodel.User, Error)
|
||||
// GetUserByExternalID returns one user with the given external id, or an error if something goes wrong.
|
||||
GetUserByExternalID(ctx context.Context, id string) (*gtsmodel.User, Error)
|
||||
// GetUserByConfirmationToken returns one user by its confirmation token, or an error if something goes wrong.
|
||||
GetUserByConfirmationToken(ctx context.Context, confirmationToken string) (*gtsmodel.User, Error)
|
||||
// PutUser will attempt to place user in the database
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue