68 lines
2.4 KiB
Go
68 lines
2.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func (s *service) identityGrant(ctx context.Context, project, provider, subject string, email *string, name, picture, challenge string) (string, error) {
|
|
name = strings.TrimSpace(name)
|
|
if len(name) > 160 {
|
|
name = ""
|
|
}
|
|
if u, err := url.Parse(picture); err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || len(picture) > 2048 {
|
|
picture = ""
|
|
}
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer tx.Rollback()
|
|
// Serialize first registration for the same provider identity.
|
|
if _, err = tx.ExecContext(ctx, "SELECT pg_advisory_xact_lock(hashtextextended($1,0))", project+":"+provider+":"+subject); err != nil {
|
|
return "", err
|
|
}
|
|
var account string
|
|
err = tx.QueryRowContext(ctx, "SELECT account_id::text FROM account_identities WHERE project=$1 AND provider=$2 AND subject=$3", project, provider, subject).Scan(&account)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
var googleSubject *string
|
|
if provider == "google" {
|
|
googleSubject = &subject
|
|
}
|
|
err = tx.QueryRowContext(ctx, `
|
|
WITH identity AS(SELECT gen_random_uuid() AS id)
|
|
INSERT INTO project_accounts(project,id,google_subject,email,username,display_name,avatar_url)
|
|
SELECT $1,id,$2,$3,
|
|
left(COALESCE(NULLIF(regexp_replace(lower($4),'[^a-z0-9]','','g'),''),'user'),7)||'_'||replace(id::text,'-',''),
|
|
$4,NULLIF($5,'') FROM identity RETURNING id::text
|
|
`, project, googleSubject, email, name, picture).Scan(&account)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = tx.ExecContext(ctx, "INSERT INTO account_identities(project,provider,subject,account_id) VALUES($1,$2,$3,$4)", project, provider, subject, account)
|
|
} else if err == nil {
|
|
_, err = tx.ExecContext(ctx, `
|
|
UPDATE project_accounts SET email=COALESCE($3,email),
|
|
display_name=CASE WHEN display_name='' THEN $4 ELSE display_name END,
|
|
avatar_url=COALESCE(avatar_url,NULLIF($5,'')),
|
|
username=COALESCE(username,left(COALESCE(NULLIF(regexp_replace(lower($4),'[^a-z0-9]','','g'),''),'user'),7)||'_'||replace(id::text,'-','')),
|
|
updated_at=now() WHERE project=$1 AND id=$2
|
|
`, project, account, email, name, picture)
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
grant := token()
|
|
_, err = tx.ExecContext(ctx, "INSERT INTO login_grants VALUES($1,$2,$3,$4,now()+interval '1 minute')", digest(grant), project, account, challenge)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err = tx.Commit(); err != nil {
|
|
return "", err
|
|
}
|
|
return grant, nil
|
|
}
|