package authentication import ( "context" "crypto/rand" "encoding/hex" "fmt" "time" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) // DevLogin mints a local session for userID without going through the real // Auth0 OAuth flow. It writes a real oauth_users/oauth_tokens row, so every // other code path (identity lookup, account linking/creation, cookie // handling) treats it exactly like a normal login. // // Only ever call this from a route gated on an explicit dev-mode flag - // never wire it up unconditionally, since it lets the caller authenticate // as any user_id with no credentials. func (a *Authenticator) DevLogin(ctx context.Context, userID, name string) (accessToken string, expiration time.Time, err error) { tokenBytes := make([]byte, 32) if _, err := rand.Read(tokenBytes); err != nil { return "", time.Time{}, fmt.Errorf("failed to generate access token: %w", err) } accessToken = "dev_" + hex.EncodeToString(tokenBytes) // Set far in the future so the near-expiry refresh path in // server/auth never tries to refresh a token that has no real // Auth0-side refresh token behind it. expiration = time.Now().Add(365 * 24 * time.Hour) if _, err = a.db.Exec( ctx, ` WITH ensured_user AS ( INSERT INTO oauth_users (user_id) VALUES (@user_id) ON CONFLICT DO NOTHING ) INSERT INTO oauth_tokens ( access_token, token_type, refresh_token, expiry, id_token_issuer, id_token_audience, id_token_subject, id_token_expiry, id_token_issued_at, id_token_nonce, id_token_access_token_hash, id_token_custom_claims_family_name, id_token_custom_claims_given_name, id_token_custom_claims_name, id_token_custom_claims_nickname, id_token_custom_claims_picture, id_token_custom_claims_updated_at ) VALUES ( @access_token, 'dev', '', @expiry, 'dev-auth-bypass', @audience, @user_id, @expiry, NOW(), '', '', @name, @name, @name, @name, '', NOW() ) `, pgx.NamedArgs{ "access_token": accessToken, "expiry": expiration, "user_id": userID, "name": name, "audience": pgtype.FlatArray[string]{"dev"}, }, ); err != nil { return "", time.Time{}, fmt.Errorf("failed to save dev session: %w", err) } return accessToken, expiration, nil }