feat: Create AuthSessions and set cookies (#4650)

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
This commit is contained in:
Maksim Nabokikh
2026-03-18 11:58:18 +01:00
committed by GitHub
parent 8af6d3c4be
commit cbd7dd7f5a
32 changed files with 1922 additions and 117 deletions
+21
View File
@@ -65,6 +65,10 @@ type Config struct {
// querying the storage. Cannot be specified without enabling a passwords
// database.
StaticPasswords []password `json:"staticPasswords"`
// Sessions holds authentication session configuration.
// Requires DEX_SESSIONS_ENABLED=true feature flag.
Sessions *Sessions `json:"sessions"`
}
// Validate the configuration
@@ -103,6 +107,11 @@ func (c Config) Validate() error {
if len(checkErrors) != 0 {
return fmt.Errorf("invalid Config:\n\t-\t%s", strings.Join(checkErrors, "\n\t-\t"))
}
if c.Sessions != nil && !featureflags.SessionsEnabled.Enabled() {
return fmt.Errorf("sessions config requires sessions to be enabled (DEX_SESSIONS_ENABLED=true)")
}
return nil
}
@@ -585,3 +594,15 @@ type RefreshToken struct {
AbsoluteLifetime string `json:"absoluteLifetime"`
ValidIfNotUsedFor string `json:"validIfNotUsedFor"`
}
// Sessions holds authentication session configuration.
type Sessions struct {
// CookieName is the name of the session cookie. Defaults to "dex_session".
CookieName string `json:"cookieName"`
// AbsoluteLifetime is the maximum session lifetime from creation. Defaults to "24h".
AbsoluteLifetime string `json:"absoluteLifetime"`
// ValidIfNotUsedFor is the idle timeout. Defaults to "1h".
ValidIfNotUsedFor string `json:"validIfNotUsedFor"`
// RememberMeCheckedByDefault controls the default state of the "remember me" checkbox.
RememberMeCheckedByDefault *bool `json:"rememberMeCheckedByDefault"`
}
+54
View File
@@ -415,6 +415,19 @@ func runServe(options serveOptions) error {
serverConfig.RefreshTokenPolicy = refreshTokenPolicy
if featureflags.SessionsEnabled.Enabled() {
sessionConfig, err := parseSessionConfig(c.Sessions)
if err != nil {
return fmt.Errorf("invalid session config: %v", err)
}
serverConfig.SessionConfig = sessionConfig
logger.Info("config sessions",
"cookie_name", sessionConfig.CookieName,
"absolute_lifetime", sessionConfig.AbsoluteLifetime,
"valid_if_not_used_for", sessionConfig.ValidIfNotUsedFor,
)
}
serverConfig.RealIPHeader = c.Web.ClientRemoteIP.Header
serverConfig.TrustedRealIPCIDRs, err = c.Web.ClientRemoteIP.ParseTrustedProxies()
if err != nil {
@@ -759,3 +772,44 @@ func loadTLSConfig(certFile, keyFile, caFile string, baseConfig *tls.Config) (*t
func recordBuildInfo() {
buildInfo.WithLabelValues(version, runtime.Version(), fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)).Set(1)
}
func parseSessionConfig(s *Sessions) (*server.SessionConfig, error) {
sc := &server.SessionConfig{
CookieName: "dex_session",
AbsoluteLifetime: 24 * time.Hour,
ValidIfNotUsedFor: 1 * time.Hour,
RememberMeCheckedByDefault: true,
}
if s != nil {
if s.CookieName != "" {
sc.CookieName = s.CookieName
}
if s.AbsoluteLifetime != "" {
d, err := time.ParseDuration(s.AbsoluteLifetime)
if err != nil {
return nil, fmt.Errorf("invalid absoluteLifetime %q: %v", s.AbsoluteLifetime, err)
}
sc.AbsoluteLifetime = d
}
if s.ValidIfNotUsedFor != "" {
d, err := time.ParseDuration(s.ValidIfNotUsedFor)
if err != nil {
return nil, fmt.Errorf("invalid validIfNotUsedFor %q: %v", s.ValidIfNotUsedFor, err)
}
sc.ValidIfNotUsedFor = d
}
if s.RememberMeCheckedByDefault != nil {
sc.RememberMeCheckedByDefault = *s.RememberMeCheckedByDefault
}
}
if sc.AbsoluteLifetime <= 0 {
return nil, fmt.Errorf("absoluteLifetime must be positive, got %v", sc.AbsoluteLifetime)
}
if sc.ValidIfNotUsedFor <= 0 {
return nil, fmt.Errorf("validIfNotUsedFor must be positive, got %v", sc.ValidIfNotUsedFor)
}
if sc.ValidIfNotUsedFor > sc.AbsoluteLifetime {
return nil, fmt.Errorf("validIfNotUsedFor (%v) must not exceed absoluteLifetime (%v)", sc.ValidIfNotUsedFor, sc.AbsoluteLifetime)
}
return sc, nil
}
+8
View File
@@ -95,6 +95,14 @@ telemetry:
# validIfNotUsedFor: "2160h" # 90 days
# absoluteLifetime: "3960h" # 165 days
# Authentication sessions configuration.
# Requires DEX_SESSIONS_ENABLED=true feature flag.
# sessions:
# cookieName: "dex_session"
# absoluteLifetime: "24h"
# validIfNotUsedFor: "1h"
# rememberMeCheckedByDefault: false
# Options for controlling the logger.
# logger:
# level: "debug"
+45 -14
View File
@@ -365,6 +365,16 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
return
}
// Check if there's a valid session that can skip login for this client.
if s.sessionConfig != nil {
if redirectURL, ok := s.trySessionLogin(ctx, r, w, authReq); ok {
if redirectURL != "" {
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
}
return
}
}
scopes := parseScopes(authReq.Scopes)
// Work out where the "Select another login method" link should go.
@@ -493,9 +503,11 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
return
}
rememberMe := s.rememberMeDefault()
switch r.Method {
case http.MethodGet:
if err := s.templates.password(r, w, r.URL.String(), "", usernamePrompt(pwConn), false, backLink); err != nil {
if err := s.templates.password(r, w, r.URL.String(), "", usernamePrompt(pwConn), false, backLink, rememberMe); err != nil {
s.logger.ErrorContext(r.Context(), "server template error", "err", err)
}
case http.MethodPost:
@@ -510,7 +522,7 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
return
}
if !ok {
if err := s.templates.password(r, w, r.URL.String(), username, usernamePrompt(pwConn), true, backLink); err != nil {
if err := s.templates.password(r, w, r.URL.String(), username, usernamePrompt(pwConn), true, backLink, rememberMe); err != nil {
s.logger.ErrorContext(r.Context(), "server template error", "err", err)
}
s.logger.ErrorContext(r.Context(), "failed login attempt: Invalid credentials.", "user", username)
@@ -523,13 +535,21 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
return
}
// Re-read auth request after finalizeLogin populated Claims.
authReq, err = s.storage.GetAuthRequest(ctx, authReq.ID)
if err != nil {
s.logger.ErrorContext(r.Context(), "failed to get finalized auth request", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
return
}
rememberMe := r.FormValue("remember_me") == "on"
if err := s.createOrUpdateAuthSession(ctx, r, w, authReq, rememberMe); err != nil {
s.logger.ErrorContext(ctx, "failed to create/update auth session", "err", err)
}
if canSkipApproval {
authReq, err = s.storage.GetAuthRequest(ctx, authReq.ID)
if err != nil {
s.logger.ErrorContext(r.Context(), "failed to get finalized auth request", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
return
}
// authReq was already re-read after finalizeLogin above.
s.sendCodeResponse(w, r, authReq)
return
}
@@ -628,13 +648,22 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
return
}
// Re-read auth request after finalizeLogin populated Claims.
authReq, err = s.storage.GetAuthRequest(ctx, authReq.ID)
if err != nil {
s.logger.ErrorContext(r.Context(), "failed to get finalized auth request", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
return
}
// Connector callbacks don't render the remember_me checkbox, so we use the server default.
// The password login handler reads r.FormValue("remember_me") from the submitted form instead.
if err := s.createOrUpdateAuthSession(ctx, r, w, authReq, s.sessionConfig != nil && s.sessionConfig.RememberMeCheckedByDefault); err != nil {
s.logger.ErrorContext(ctx, "failed to create/update auth session", "err", err)
}
if canSkipApproval {
authReq, err = s.storage.GetAuthRequest(ctx, authReq.ID)
if err != nil {
s.logger.ErrorContext(r.Context(), "failed to get finalized auth request", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
return
}
// authReq was already re-read after finalizeLogin above.
s.sendCodeResponse(w, r, authReq)
return
}
@@ -854,6 +883,8 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authReq storage.AuthRequest) {
s.updateSessionTokenIssuedAt(r, authReq.ClientID)
ctx := r.Context()
if s.now().After(authReq.Expiry) {
s.renderError(r, w, http.StatusBadRequest, "User session has expired.")
+14
View File
@@ -137,6 +137,17 @@ type Config struct {
// If enabled, the server will continue starting even if some connectors fail to initialize.
// This allows the server to operate with a subset of connectors if some are misconfigured.
ContinueOnConnectorFailure bool
// SessionConfig holds session settings. Nil when sessions are disabled.
SessionConfig *SessionConfig
}
// SessionConfig holds resolved session configuration.
type SessionConfig struct {
CookieName string
AbsoluteLifetime time.Duration
ValidIfNotUsedFor time.Duration
RememberMeCheckedByDefault bool
}
// WebConfig holds the server's frontend templates and asset configuration.
@@ -226,6 +237,8 @@ type Server struct {
logger *slog.Logger
signer signer.Signer
sessionConfig *SessionConfig
}
// NewServer constructs a server from the provided config.
@@ -349,6 +362,7 @@ func newServer(ctx context.Context, c Config) (*Server, error) {
passwordConnector: c.PasswordConnector,
logger: c.Logger,
signer: c.Signer,
sessionConfig: c.SessionConfig,
}
// Retrieves connector objects in backend storage. This list includes the static connectors
+339
View File
@@ -0,0 +1,339 @@
package server
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/http"
"path"
"strings"
"github.com/dexidp/dex/storage"
)
// rememberMeDefault returns a pointer to the default remember-me value if sessions are enabled, nil otherwise.
func (s *Server) rememberMeDefault() *bool {
if s.sessionConfig == nil {
return nil
}
v := s.sessionConfig.RememberMeCheckedByDefault
return &v
}
// remoteIP returns the real IP from context (set by parseRealIP middleware) or falls back to r.RemoteAddr.
func remoteIP(r *http.Request) string {
if ip, ok := r.Context().Value(RequestKeyRemoteIP).(string); ok && ip != "" {
return ip
}
return r.RemoteAddr
}
// sessionCookieValue encodes session identity into a cookie value.
// Format: base64url(userID) + "." + base64url(connectorID) + "." + nonce
// TODO(nabokihms): consider cookie encoding
func sessionCookieValue(userID, connectorID, nonce string) string {
return base64.RawURLEncoding.EncodeToString([]byte(userID)) +
"." + base64.RawURLEncoding.EncodeToString([]byte(connectorID)) +
"." + nonce
}
// parseSessionCookie decodes a session cookie value into its components.
func parseSessionCookie(value string) (userID, connectorID, nonce string, err error) {
parts := strings.SplitN(value, ".", 3)
if len(parts) != 3 {
return "", "", "", fmt.Errorf("invalid session cookie format")
}
userIDBytes, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return "", "", "", fmt.Errorf("decode userID: %w", err)
}
connectorIDBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return "", "", "", fmt.Errorf("decode connectorID: %w", err)
}
return string(userIDBytes), string(connectorIDBytes), parts[2], nil
}
func (s *Server) sessionCookiePath() string {
if s.issuerURL.Path == "" {
return "/"
}
return s.issuerURL.Path
}
func (s *Server) setSessionCookie(w http.ResponseWriter, userID, connectorID, nonce string, rememberMe bool) {
cookie := &http.Cookie{
Name: s.sessionConfig.CookieName,
Value: sessionCookieValue(userID, connectorID, nonce),
Path: s.sessionCookiePath(),
HttpOnly: true,
Secure: s.issuerURL.Scheme == "https",
SameSite: http.SameSiteLaxMode,
}
if rememberMe {
cookie.MaxAge = int(s.sessionConfig.AbsoluteLifetime.Seconds())
}
http.SetCookie(w, cookie)
}
func (s *Server) clearSessionCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: s.sessionConfig.CookieName,
Value: "",
Path: s.sessionCookiePath(),
HttpOnly: true,
Secure: s.issuerURL.Scheme == "https",
SameSite: http.SameSiteLaxMode,
MaxAge: -1,
})
}
// getValidAuthSession returns a valid, non-expired session or nil.
// It parses the session cookie to extract (userID, connectorID, nonce),
// looks up the session by composite key, and verifies the nonce.
// Invalid or expired session cookies are cleared automatically.
func (s *Server) getValidAuthSession(ctx context.Context, w http.ResponseWriter, r *http.Request, authReq *storage.AuthRequest) *storage.AuthSession {
if s.sessionConfig == nil {
return nil
}
cookie, err := r.Cookie(s.sessionConfig.CookieName)
if err != nil || cookie.Value == "" {
return nil
}
userID, connectorID, nonce, err := parseSessionCookie(cookie.Value)
if err != nil {
s.logger.DebugContext(ctx, "invalid session cookie format", "err", err)
s.clearSessionCookie(w)
return nil
}
session, err := s.storage.GetAuthSession(ctx, userID, connectorID)
if err != nil {
if !errors.Is(err, storage.ErrNotFound) {
s.logger.ErrorContext(ctx, "failed to get auth session", "err", err)
}
s.clearSessionCookie(w)
return nil
}
// Verify nonce to prevent cookie forgery.
if session.Nonce != nonce {
s.logger.DebugContext(ctx, "auth session nonce mismatch")
s.clearSessionCookie(w)
return nil
}
now := s.now()
// Check absolute lifetime.
if now.After(session.CreatedAt.Add(s.sessionConfig.AbsoluteLifetime)) {
s.logger.InfoContext(ctx, "auth session expired (absolute lifetime)",
"user_id", session.UserID, "connector_id", session.ConnectorID)
if err := s.storage.DeleteAuthSession(ctx, session.UserID, session.ConnectorID); err != nil {
s.logger.DebugContext(ctx, "failed to delete expired auth session", "err", err)
}
s.clearSessionCookie(w)
return nil
}
// Check idle timeout.
if now.After(session.LastActivity.Add(s.sessionConfig.ValidIfNotUsedFor)) {
s.logger.InfoContext(ctx, "auth session expired (idle timeout)",
"user_id", session.UserID, "connector_id", session.ConnectorID)
if err := s.storage.DeleteAuthSession(ctx, session.UserID, session.ConnectorID); err != nil {
s.logger.DebugContext(ctx, "failed to delete expired auth session", "err", err)
}
s.clearSessionCookie(w)
return nil
}
// Only reuse sessions from the same connector.
if session.ConnectorID != authReq.ConnectorID {
return nil
}
return &session
}
// createOrUpdateAuthSession creates a new session or updates an existing one
// after a successful login, and sets the session cookie.
// rememberMe controls whether the cookie is persistent (survives browser close).
func (s *Server) createOrUpdateAuthSession(ctx context.Context, r *http.Request, w http.ResponseWriter, authReq storage.AuthRequest, rememberMe bool) error {
if s.sessionConfig == nil {
return nil
}
now := s.now()
userID := authReq.Claims.UserID
connectorID := authReq.ConnectorID
clientState := &storage.ClientAuthState{
Active: true,
ExpiresAt: now.Add(s.sessionConfig.AbsoluteLifetime),
LastActivity: now,
}
// Try to reuse existing session for this (userID, connectorID).
session, err := s.storage.GetAuthSession(ctx, userID, connectorID)
if err == nil {
// Session exists, update it.
s.logger.DebugContext(ctx, "updating existing auth session",
"user_id", userID, "connector_id", connectorID, "client_id", authReq.ClientID)
if err := s.storage.UpdateAuthSession(ctx, userID, connectorID, func(old storage.AuthSession) (storage.AuthSession, error) {
old.LastActivity = now
if old.ClientStates == nil {
old.ClientStates = make(map[string]*storage.ClientAuthState)
}
old.ClientStates[authReq.ClientID] = clientState
return old, nil
}); err != nil {
return fmt.Errorf("update auth session: %w", err)
}
s.setSessionCookie(w, userID, connectorID, session.Nonce, rememberMe)
return nil
}
// Unexpected error, exit the method.
if !errors.Is(err, storage.ErrNotFound) {
return fmt.Errorf("get auth session: %w", err)
}
nonce := storage.NewID()
newSession := storage.AuthSession{
UserID: userID,
ConnectorID: connectorID,
Nonce: nonce,
ClientStates: map[string]*storage.ClientAuthState{
authReq.ClientID: clientState,
},
CreatedAt: now,
LastActivity: now,
IPAddress: remoteIP(r),
UserAgent: r.UserAgent(),
}
if err := s.storage.CreateAuthSession(ctx, newSession); err != nil {
return fmt.Errorf("create auth session: %w", err)
}
s.logger.DebugContext(ctx, "created new auth session",
"user_id", userID, "connector_id", connectorID, "client_id", authReq.ClientID)
s.setSessionCookie(w, userID, connectorID, nonce, rememberMe)
return nil
}
// trySessionLogin checks if the user has a valid session for the same connector.
// If so, it finalizes login from the stored identity and returns a redirect URL.
// Returns ("", false) if session-based login is not possible.
func (s *Server) trySessionLogin(ctx context.Context, r *http.Request, w http.ResponseWriter, authReq *storage.AuthRequest) (string, bool) {
session := s.getValidAuthSession(ctx, w, r, authReq)
if session == nil {
return "", false
}
clientState, ok := session.ClientStates[authReq.ClientID]
if !ok || !clientState.Active {
return "", false
}
now := s.now()
if now.After(clientState.ExpiresAt) {
return "", false
}
// Load identity from storage.
ui, err := s.storage.GetUserIdentity(ctx, session.UserID, session.ConnectorID)
if err != nil {
s.logger.ErrorContext(ctx, "session: failed to get user identity", "err", err)
return "", false
}
claims := storage.Claims{
UserID: ui.Claims.UserID,
Username: ui.Claims.Username,
PreferredUsername: ui.Claims.PreferredUsername,
Email: ui.Claims.Email,
EmailVerified: ui.Claims.EmailVerified,
Groups: ui.Claims.Groups,
}
// Update AuthRequest with stored identity (without logging "login successful").
if err := s.storage.UpdateAuthRequest(ctx, authReq.ID, func(a storage.AuthRequest) (storage.AuthRequest, error) {
a.LoggedIn = true
a.Claims = claims
a.ConnectorID = session.ConnectorID
return a, nil
}); err != nil {
s.logger.ErrorContext(ctx, "session: failed to update auth request", "err", err)
return "", false
}
s.logger.DebugContext(ctx, "session: re-authenticated from session",
"user_id", session.UserID, "connector_id", session.ConnectorID)
// Update session activity.
_ = s.storage.UpdateAuthSession(ctx, session.UserID, session.ConnectorID, func(old storage.AuthSession) (storage.AuthSession, error) {
old.LastActivity = now
if cs, ok := old.ClientStates[authReq.ClientID]; ok {
cs.LastActivity = now
}
return old, nil
})
// Build HMAC for approval URL.
h := hmac.New(sha256.New, authReq.HMACKey)
h.Write([]byte(authReq.ID))
mac := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
// Skip approval if globally configured or user already consented to the requested scopes.
if !authReq.ForceApprovalPrompt && (s.skipApproval || scopesCoveredByConsent(ui.Consents[authReq.ClientID], authReq.Scopes)) {
// Re-read to get the updated AuthRequest (LoggedIn, Claims, ConnectorID set above).
updated, err := s.storage.GetAuthRequest(ctx, authReq.ID)
if err != nil {
s.logger.ErrorContext(ctx, "session: failed to get auth request", "err", err)
return "", false
}
s.sendCodeResponse(w, r, updated)
return "", true
}
returnURL := path.Join(s.issuerURL.Path, "/approval") + "?req=" + authReq.ID + "&hmac=" + mac
return returnURL, true
}
// updateSessionTokenIssuedAt updates the session's LastTokenIssuedAt for the given client.
func (s *Server) updateSessionTokenIssuedAt(r *http.Request, clientID string) {
if s.sessionConfig == nil {
return
}
cookie, err := r.Cookie(s.sessionConfig.CookieName)
if err != nil || cookie.Value == "" {
return
}
userID, connectorID, _, err := parseSessionCookie(cookie.Value)
if err != nil {
return
}
now := s.now()
_ = s.storage.UpdateAuthSession(r.Context(), userID, connectorID, func(old storage.AuthSession) (storage.AuthSession, error) {
old.LastActivity = now
if cs, ok := old.ClientStates[clientID]; ok {
cs.LastTokenIssuedAt = now
cs.LastActivity = now
}
return old, nil
})
}
File diff suppressed because it is too large Load Diff
+21 -8
View File
@@ -291,18 +291,31 @@ func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []c
return renderTemplate(w, t.loginTmpl, data)
}
func (t *templates) password(r *http.Request, w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid bool, backLink string) error {
func (t *templates) password(r *http.Request, w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid bool, backLink string, rememberMe *bool) error {
if lastWasInvalid {
w.WriteHeader(http.StatusUnauthorized)
}
data := struct {
PostURL string
BackLink string
Username string
UsernamePrompt string
Invalid bool
ReqPath string
}{postURL, backLink, lastUsername, usernamePrompt, lastWasInvalid, r.URL.Path}
PostURL string
BackLink string
Username string
UsernamePrompt string
Invalid bool
ReqPath string
ShowRememberMe bool
RememberMeChecked bool
}{
PostURL: postURL,
BackLink: backLink,
Username: lastUsername,
UsernamePrompt: usernamePrompt,
Invalid: lastWasInvalid,
ReqPath: r.URL.Path,
ShowRememberMe: rememberMe != nil,
}
if rememberMe != nil {
data.RememberMeChecked = *rememberMe
}
return renderTemplate(w, t.passwordTmpl, data)
}
+8 -13
View File
@@ -1174,11 +1174,11 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
now := time.Now().UTC().Round(time.Millisecond)
session := storage.AuthSession{
ID: storage.NewID(),
UserID: "user1",
ConnectorID: "conn1",
Nonce: storage.NewID(),
ClientStates: map[string]*storage.ClientAuthState{
"client1": {
UserID: "user1",
ConnectorID: "conn1",
Active: true,
ExpiresAt: now.Add(24 * time.Hour),
LastActivity: now,
@@ -1201,7 +1201,7 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
mustBeErrAlreadyExists(t, "auth session", err)
// Get and compare.
got, err := s.GetAuthSession(ctx, session.ID)
got, err := s.GetAuthSession(ctx, session.UserID, session.ConnectorID)
if err != nil {
t.Fatalf("get auth session: %v", err)
}
@@ -1219,10 +1219,8 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
// Update: add a new client state.
newNow := now.Add(time.Minute)
if err := s.UpdateAuthSession(ctx, session.ID, func(old storage.AuthSession) (storage.AuthSession, error) {
if err := s.UpdateAuthSession(ctx, session.UserID, session.ConnectorID, func(old storage.AuthSession) (storage.AuthSession, error) {
old.ClientStates["client2"] = &storage.ClientAuthState{
UserID: "user2",
ConnectorID: "conn2",
Active: true,
ExpiresAt: newNow.Add(24 * time.Hour),
LastActivity: newNow,
@@ -1234,7 +1232,7 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
}
// Get and verify update.
got, err = s.GetAuthSession(ctx, session.ID)
got, err = s.GetAuthSession(ctx, session.UserID, session.ConnectorID)
if err != nil {
t.Fatalf("get auth session after update: %v", err)
}
@@ -1244,9 +1242,6 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
if got.ClientStates["client2"] == nil {
t.Fatal("expected client2 state to exist")
}
if got.ClientStates["client2"].UserID != "user2" {
t.Errorf("expected client2 user_id to be user2, got %s", got.ClientStates["client2"].UserID)
}
// List and verify.
sessions, err := s.ListAuthSessions(ctx)
@@ -1258,11 +1253,11 @@ func testAuthSessionCRUD(t *testing.T, s storage.Storage) {
}
// Delete.
if err := s.DeleteAuthSession(ctx, session.ID); err != nil {
if err := s.DeleteAuthSession(ctx, session.UserID, session.ConnectorID); err != nil {
t.Fatalf("delete auth session: %v", err)
}
// Get deleted should return ErrNotFound.
_, err = s.GetAuthSession(ctx, session.ID)
_, err = s.GetAuthSession(ctx, session.UserID, session.ConnectorID)
mustBeErrNotFound(t, "auth session", err)
}
+17 -10
View File
@@ -18,8 +18,12 @@ func (d *Database) CreateAuthSession(ctx context.Context, session storage.AuthSe
return fmt.Errorf("encode client states auth session: %w", err)
}
id := compositeKeyID(session.UserID, session.ConnectorID, d.hasher)
_, err = d.client.AuthSession.Create().
SetID(session.ID).
SetID(id).
SetUserID(session.UserID).
SetConnectorID(session.ConnectorID).
SetNonce(session.Nonce).
SetClientStates(encodedStates).
SetCreatedAt(session.CreatedAt).
SetLastActivity(session.LastActivity).
@@ -32,9 +36,10 @@ func (d *Database) CreateAuthSession(ctx context.Context, session storage.AuthSe
return nil
}
// GetAuthSession extracts an auth session from the database by session ID.
func (d *Database) GetAuthSession(ctx context.Context, sessionID string) (storage.AuthSession, error) {
authSession, err := d.client.AuthSession.Get(ctx, sessionID)
// GetAuthSession extracts an auth session from the database by user ID and connector ID.
func (d *Database) GetAuthSession(ctx context.Context, userID, connectorID string) (storage.AuthSession, error) {
id := compositeKeyID(userID, connectorID, d.hasher)
authSession, err := d.client.AuthSession.Get(ctx, id)
if err != nil {
return storage.AuthSession{}, convertDBError("get auth session: %w", err)
}
@@ -55,9 +60,10 @@ func (d *Database) ListAuthSessions(ctx context.Context) ([]storage.AuthSession,
return storageAuthSessions, nil
}
// DeleteAuthSession deletes an auth session from the database by session ID.
func (d *Database) DeleteAuthSession(ctx context.Context, sessionID string) error {
err := d.client.AuthSession.DeleteOneID(sessionID).Exec(ctx)
// DeleteAuthSession deletes an auth session from the database by user ID and connector ID.
func (d *Database) DeleteAuthSession(ctx context.Context, userID, connectorID string) error {
id := compositeKeyID(userID, connectorID, d.hasher)
err := d.client.AuthSession.DeleteOneID(id).Exec(ctx)
if err != nil {
return convertDBError("delete auth session: %w", err)
}
@@ -65,13 +71,14 @@ func (d *Database) DeleteAuthSession(ctx context.Context, sessionID string) erro
}
// UpdateAuthSession changes an auth session using an updater function.
func (d *Database) UpdateAuthSession(ctx context.Context, sessionID string, updater func(s storage.AuthSession) (storage.AuthSession, error)) error {
func (d *Database) UpdateAuthSession(ctx context.Context, userID, connectorID string, updater func(s storage.AuthSession) (storage.AuthSession, error)) error {
id := compositeKeyID(userID, connectorID, d.hasher)
tx, err := d.BeginTx(ctx)
if err != nil {
return convertDBError("update auth session tx: %w", err)
}
authSession, err := tx.AuthSession.Get(ctx, sessionID)
authSession, err := tx.AuthSession.Get(ctx, id)
if err != nil {
return rollback(tx, "update auth session database: %w", err)
}
@@ -90,7 +97,7 @@ func (d *Database) UpdateAuthSession(ctx context.Context, sessionID string, upda
return rollback(tx, "encode client states auth session: %w", err)
}
_, err = tx.AuthSession.UpdateOneID(sessionID).
_, err = tx.AuthSession.UpdateOneID(id).
SetClientStates(encodedStates).
SetLastActivity(newSession.LastActivity).
SetIPAddress(newSession.IPAddress).
+3 -1
View File
@@ -198,7 +198,9 @@ func toStorageUserIdentity(u *db.UserIdentity) storage.UserIdentity {
func toStorageAuthSession(s *db.AuthSession) storage.AuthSession {
result := storage.AuthSession{
ID: s.ID,
UserID: s.UserID,
ConnectorID: s.ConnectorID,
Nonce: s.Nonce,
CreatedAt: s.CreatedAt,
LastActivity: s.LastActivity,
IPAddress: s.IPAddress,
+34 -1
View File
@@ -17,6 +17,12 @@ type AuthSession struct {
config `json:"-"`
// ID of the ent.
ID string `json:"id,omitempty"`
// UserID holds the value of the "user_id" field.
UserID string `json:"user_id,omitempty"`
// ConnectorID holds the value of the "connector_id" field.
ConnectorID string `json:"connector_id,omitempty"`
// Nonce holds the value of the "nonce" field.
Nonce string `json:"nonce,omitempty"`
// ClientStates holds the value of the "client_states" field.
ClientStates []byte `json:"client_states,omitempty"`
// CreatedAt holds the value of the "created_at" field.
@@ -37,7 +43,7 @@ func (*AuthSession) scanValues(columns []string) ([]any, error) {
switch columns[i] {
case authsession.FieldClientStates:
values[i] = new([]byte)
case authsession.FieldID, authsession.FieldIPAddress, authsession.FieldUserAgent:
case authsession.FieldID, authsession.FieldUserID, authsession.FieldConnectorID, authsession.FieldNonce, authsession.FieldIPAddress, authsession.FieldUserAgent:
values[i] = new(sql.NullString)
case authsession.FieldCreatedAt, authsession.FieldLastActivity:
values[i] = new(sql.NullTime)
@@ -62,6 +68,24 @@ func (_m *AuthSession) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.ID = value.String
}
case authsession.FieldUserID:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field user_id", values[i])
} else if value.Valid {
_m.UserID = value.String
}
case authsession.FieldConnectorID:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field connector_id", values[i])
} else if value.Valid {
_m.ConnectorID = value.String
}
case authsession.FieldNonce:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field nonce", values[i])
} else if value.Valid {
_m.Nonce = value.String
}
case authsession.FieldClientStates:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field client_states", values[i])
@@ -128,6 +152,15 @@ func (_m *AuthSession) String() string {
var builder strings.Builder
builder.WriteString("AuthSession(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("user_id=")
builder.WriteString(_m.UserID)
builder.WriteString(", ")
builder.WriteString("connector_id=")
builder.WriteString(_m.ConnectorID)
builder.WriteString(", ")
builder.WriteString("nonce=")
builder.WriteString(_m.Nonce)
builder.WriteString(", ")
builder.WriteString("client_states=")
builder.WriteString(fmt.Sprintf("%v", _m.ClientStates))
builder.WriteString(", ")
+30
View File
@@ -11,6 +11,12 @@ const (
Label = "auth_session"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldUserID holds the string denoting the user_id field in the database.
FieldUserID = "user_id"
// FieldConnectorID holds the string denoting the connector_id field in the database.
FieldConnectorID = "connector_id"
// FieldNonce holds the string denoting the nonce field in the database.
FieldNonce = "nonce"
// FieldClientStates holds the string denoting the client_states field in the database.
FieldClientStates = "client_states"
// FieldCreatedAt holds the string denoting the created_at field in the database.
@@ -28,6 +34,9 @@ const (
// Columns holds all SQL columns for authsession fields.
var Columns = []string{
FieldID,
FieldUserID,
FieldConnectorID,
FieldNonce,
FieldClientStates,
FieldCreatedAt,
FieldLastActivity,
@@ -46,6 +55,12 @@ func ValidColumn(column string) bool {
}
var (
// UserIDValidator is a validator for the "user_id" field. It is called by the builders before save.
UserIDValidator func(string) error
// ConnectorIDValidator is a validator for the "connector_id" field. It is called by the builders before save.
ConnectorIDValidator func(string) error
// NonceValidator is a validator for the "nonce" field. It is called by the builders before save.
NonceValidator func(string) error
// DefaultIPAddress holds the default value on creation for the "ip_address" field.
DefaultIPAddress string
// DefaultUserAgent holds the default value on creation for the "user_agent" field.
@@ -62,6 +77,21 @@ func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByUserID orders the results by the user_id field.
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUserID, opts...).ToFunc()
}
// ByConnectorID orders the results by the connector_id field.
func ByConnectorID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldConnectorID, opts...).ToFunc()
}
// ByNonce orders the results by the nonce field.
func ByNonce(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldNonce, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+210
View File
@@ -64,6 +64,21 @@ func IDContainsFold(id string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContainsFold(FieldID, id))
}
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
func UserID(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldUserID, v))
}
// ConnectorID applies equality check predicate on the "connector_id" field. It's identical to ConnectorIDEQ.
func ConnectorID(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldConnectorID, v))
}
// Nonce applies equality check predicate on the "nonce" field. It's identical to NonceEQ.
func Nonce(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldNonce, v))
}
// ClientStates applies equality check predicate on the "client_states" field. It's identical to ClientStatesEQ.
func ClientStates(v []byte) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldClientStates, v))
@@ -89,6 +104,201 @@ func UserAgent(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldUserAgent, v))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldUserID, v))
}
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
func UserIDNEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNEQ(FieldUserID, v))
}
// UserIDIn applies the In predicate on the "user_id" field.
func UserIDIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldIn(FieldUserID, vs...))
}
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
func UserIDNotIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNotIn(FieldUserID, vs...))
}
// UserIDGT applies the GT predicate on the "user_id" field.
func UserIDGT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGT(FieldUserID, v))
}
// UserIDGTE applies the GTE predicate on the "user_id" field.
func UserIDGTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGTE(FieldUserID, v))
}
// UserIDLT applies the LT predicate on the "user_id" field.
func UserIDLT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLT(FieldUserID, v))
}
// UserIDLTE applies the LTE predicate on the "user_id" field.
func UserIDLTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLTE(FieldUserID, v))
}
// UserIDContains applies the Contains predicate on the "user_id" field.
func UserIDContains(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContains(FieldUserID, v))
}
// UserIDHasPrefix applies the HasPrefix predicate on the "user_id" field.
func UserIDHasPrefix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasPrefix(FieldUserID, v))
}
// UserIDHasSuffix applies the HasSuffix predicate on the "user_id" field.
func UserIDHasSuffix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasSuffix(FieldUserID, v))
}
// UserIDEqualFold applies the EqualFold predicate on the "user_id" field.
func UserIDEqualFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEqualFold(FieldUserID, v))
}
// UserIDContainsFold applies the ContainsFold predicate on the "user_id" field.
func UserIDContainsFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContainsFold(FieldUserID, v))
}
// ConnectorIDEQ applies the EQ predicate on the "connector_id" field.
func ConnectorIDEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldConnectorID, v))
}
// ConnectorIDNEQ applies the NEQ predicate on the "connector_id" field.
func ConnectorIDNEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNEQ(FieldConnectorID, v))
}
// ConnectorIDIn applies the In predicate on the "connector_id" field.
func ConnectorIDIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldIn(FieldConnectorID, vs...))
}
// ConnectorIDNotIn applies the NotIn predicate on the "connector_id" field.
func ConnectorIDNotIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNotIn(FieldConnectorID, vs...))
}
// ConnectorIDGT applies the GT predicate on the "connector_id" field.
func ConnectorIDGT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGT(FieldConnectorID, v))
}
// ConnectorIDGTE applies the GTE predicate on the "connector_id" field.
func ConnectorIDGTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGTE(FieldConnectorID, v))
}
// ConnectorIDLT applies the LT predicate on the "connector_id" field.
func ConnectorIDLT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLT(FieldConnectorID, v))
}
// ConnectorIDLTE applies the LTE predicate on the "connector_id" field.
func ConnectorIDLTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLTE(FieldConnectorID, v))
}
// ConnectorIDContains applies the Contains predicate on the "connector_id" field.
func ConnectorIDContains(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContains(FieldConnectorID, v))
}
// ConnectorIDHasPrefix applies the HasPrefix predicate on the "connector_id" field.
func ConnectorIDHasPrefix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasPrefix(FieldConnectorID, v))
}
// ConnectorIDHasSuffix applies the HasSuffix predicate on the "connector_id" field.
func ConnectorIDHasSuffix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasSuffix(FieldConnectorID, v))
}
// ConnectorIDEqualFold applies the EqualFold predicate on the "connector_id" field.
func ConnectorIDEqualFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEqualFold(FieldConnectorID, v))
}
// ConnectorIDContainsFold applies the ContainsFold predicate on the "connector_id" field.
func ConnectorIDContainsFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContainsFold(FieldConnectorID, v))
}
// NonceEQ applies the EQ predicate on the "nonce" field.
func NonceEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldNonce, v))
}
// NonceNEQ applies the NEQ predicate on the "nonce" field.
func NonceNEQ(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNEQ(FieldNonce, v))
}
// NonceIn applies the In predicate on the "nonce" field.
func NonceIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldIn(FieldNonce, vs...))
}
// NonceNotIn applies the NotIn predicate on the "nonce" field.
func NonceNotIn(vs ...string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldNotIn(FieldNonce, vs...))
}
// NonceGT applies the GT predicate on the "nonce" field.
func NonceGT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGT(FieldNonce, v))
}
// NonceGTE applies the GTE predicate on the "nonce" field.
func NonceGTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldGTE(FieldNonce, v))
}
// NonceLT applies the LT predicate on the "nonce" field.
func NonceLT(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLT(FieldNonce, v))
}
// NonceLTE applies the LTE predicate on the "nonce" field.
func NonceLTE(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldLTE(FieldNonce, v))
}
// NonceContains applies the Contains predicate on the "nonce" field.
func NonceContains(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContains(FieldNonce, v))
}
// NonceHasPrefix applies the HasPrefix predicate on the "nonce" field.
func NonceHasPrefix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasPrefix(FieldNonce, v))
}
// NonceHasSuffix applies the HasSuffix predicate on the "nonce" field.
func NonceHasSuffix(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldHasSuffix(FieldNonce, v))
}
// NonceEqualFold applies the EqualFold predicate on the "nonce" field.
func NonceEqualFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEqualFold(FieldNonce, v))
}
// NonceContainsFold applies the ContainsFold predicate on the "nonce" field.
func NonceContainsFold(v string) predicate.AuthSession {
return predicate.AuthSession(sql.FieldContainsFold(FieldNonce, v))
}
// ClientStatesEQ applies the EQ predicate on the "client_states" field.
func ClientStatesEQ(v []byte) predicate.AuthSession {
return predicate.AuthSession(sql.FieldEQ(FieldClientStates, v))
+54
View File
@@ -20,6 +20,24 @@ type AuthSessionCreate struct {
hooks []Hook
}
// SetUserID sets the "user_id" field.
func (_c *AuthSessionCreate) SetUserID(v string) *AuthSessionCreate {
_c.mutation.SetUserID(v)
return _c
}
// SetConnectorID sets the "connector_id" field.
func (_c *AuthSessionCreate) SetConnectorID(v string) *AuthSessionCreate {
_c.mutation.SetConnectorID(v)
return _c
}
// SetNonce sets the "nonce" field.
func (_c *AuthSessionCreate) SetNonce(v string) *AuthSessionCreate {
_c.mutation.SetNonce(v)
return _c
}
// SetClientStates sets the "client_states" field.
func (_c *AuthSessionCreate) SetClientStates(v []byte) *AuthSessionCreate {
_c.mutation.SetClientStates(v)
@@ -119,6 +137,30 @@ func (_c *AuthSessionCreate) defaults() {
// check runs all checks and user-defined validators on the builder.
func (_c *AuthSessionCreate) check() error {
if _, ok := _c.mutation.UserID(); !ok {
return &ValidationError{Name: "user_id", err: errors.New(`db: missing required field "AuthSession.user_id"`)}
}
if v, ok := _c.mutation.UserID(); ok {
if err := authsession.UserIDValidator(v); err != nil {
return &ValidationError{Name: "user_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.user_id": %w`, err)}
}
}
if _, ok := _c.mutation.ConnectorID(); !ok {
return &ValidationError{Name: "connector_id", err: errors.New(`db: missing required field "AuthSession.connector_id"`)}
}
if v, ok := _c.mutation.ConnectorID(); ok {
if err := authsession.ConnectorIDValidator(v); err != nil {
return &ValidationError{Name: "connector_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.connector_id": %w`, err)}
}
}
if _, ok := _c.mutation.Nonce(); !ok {
return &ValidationError{Name: "nonce", err: errors.New(`db: missing required field "AuthSession.nonce"`)}
}
if v, ok := _c.mutation.Nonce(); ok {
if err := authsession.NonceValidator(v); err != nil {
return &ValidationError{Name: "nonce", err: fmt.Errorf(`db: validator failed for field "AuthSession.nonce": %w`, err)}
}
}
if _, ok := _c.mutation.ClientStates(); !ok {
return &ValidationError{Name: "client_states", err: errors.New(`db: missing required field "AuthSession.client_states"`)}
}
@@ -174,6 +216,18 @@ func (_c *AuthSessionCreate) createSpec() (*AuthSession, *sqlgraph.CreateSpec) {
_node.ID = id
_spec.ID.Value = id
}
if value, ok := _c.mutation.UserID(); ok {
_spec.SetField(authsession.FieldUserID, field.TypeString, value)
_node.UserID = value
}
if value, ok := _c.mutation.ConnectorID(); ok {
_spec.SetField(authsession.FieldConnectorID, field.TypeString, value)
_node.ConnectorID = value
}
if value, ok := _c.mutation.Nonce(); ok {
_spec.SetField(authsession.FieldNonce, field.TypeString, value)
_node.Nonce = value
}
if value, ok := _c.mutation.ClientStates(); ok {
_spec.SetField(authsession.FieldClientStates, field.TypeBytes, value)
_node.ClientStates = value
+4 -4
View File
@@ -262,12 +262,12 @@ func (_q *AuthSessionQuery) Clone() *AuthSessionQuery {
// Example:
//
// var v []struct {
// ClientStates []byte `json:"client_states,omitempty"`
// UserID string `json:"user_id,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.AuthSession.Query().
// GroupBy(authsession.FieldClientStates).
// GroupBy(authsession.FieldUserID).
// Aggregate(db.Count()).
// Scan(ctx, &v)
func (_q *AuthSessionQuery) GroupBy(field string, fields ...string) *AuthSessionGroupBy {
@@ -285,11 +285,11 @@ func (_q *AuthSessionQuery) GroupBy(field string, fields ...string) *AuthSession
// Example:
//
// var v []struct {
// ClientStates []byte `json:"client_states,omitempty"`
// UserID string `json:"user_id,omitempty"`
// }
//
// client.AuthSession.Query().
// Select(authsession.FieldClientStates).
// Select(authsession.FieldUserID).
// Scan(ctx, &v)
func (_q *AuthSessionQuery) Select(fields ...string) *AuthSessionSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
+148
View File
@@ -28,6 +28,48 @@ func (_u *AuthSessionUpdate) Where(ps ...predicate.AuthSession) *AuthSessionUpda
return _u
}
// SetUserID sets the "user_id" field.
func (_u *AuthSessionUpdate) SetUserID(v string) *AuthSessionUpdate {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *AuthSessionUpdate) SetNillableUserID(v *string) *AuthSessionUpdate {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetConnectorID sets the "connector_id" field.
func (_u *AuthSessionUpdate) SetConnectorID(v string) *AuthSessionUpdate {
_u.mutation.SetConnectorID(v)
return _u
}
// SetNillableConnectorID sets the "connector_id" field if the given value is not nil.
func (_u *AuthSessionUpdate) SetNillableConnectorID(v *string) *AuthSessionUpdate {
if v != nil {
_u.SetConnectorID(*v)
}
return _u
}
// SetNonce sets the "nonce" field.
func (_u *AuthSessionUpdate) SetNonce(v string) *AuthSessionUpdate {
_u.mutation.SetNonce(v)
return _u
}
// SetNillableNonce sets the "nonce" field if the given value is not nil.
func (_u *AuthSessionUpdate) SetNillableNonce(v *string) *AuthSessionUpdate {
if v != nil {
_u.SetNonce(*v)
}
return _u
}
// SetClientStates sets the "client_states" field.
func (_u *AuthSessionUpdate) SetClientStates(v []byte) *AuthSessionUpdate {
_u.mutation.SetClientStates(v)
@@ -122,7 +164,30 @@ func (_u *AuthSessionUpdate) ExecX(ctx context.Context) {
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *AuthSessionUpdate) check() error {
if v, ok := _u.mutation.UserID(); ok {
if err := authsession.UserIDValidator(v); err != nil {
return &ValidationError{Name: "user_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.user_id": %w`, err)}
}
}
if v, ok := _u.mutation.ConnectorID(); ok {
if err := authsession.ConnectorIDValidator(v); err != nil {
return &ValidationError{Name: "connector_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.connector_id": %w`, err)}
}
}
if v, ok := _u.mutation.Nonce(); ok {
if err := authsession.NonceValidator(v); err != nil {
return &ValidationError{Name: "nonce", err: fmt.Errorf(`db: validator failed for field "AuthSession.nonce": %w`, err)}
}
}
return nil
}
func (_u *AuthSessionUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(authsession.Table, authsession.Columns, sqlgraph.NewFieldSpec(authsession.FieldID, field.TypeString))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
@@ -131,6 +196,15 @@ func (_u *AuthSessionUpdate) sqlSave(ctx context.Context) (_node int, err error)
}
}
}
if value, ok := _u.mutation.UserID(); ok {
_spec.SetField(authsession.FieldUserID, field.TypeString, value)
}
if value, ok := _u.mutation.ConnectorID(); ok {
_spec.SetField(authsession.FieldConnectorID, field.TypeString, value)
}
if value, ok := _u.mutation.Nonce(); ok {
_spec.SetField(authsession.FieldNonce, field.TypeString, value)
}
if value, ok := _u.mutation.ClientStates(); ok {
_spec.SetField(authsession.FieldClientStates, field.TypeBytes, value)
}
@@ -166,6 +240,48 @@ type AuthSessionUpdateOne struct {
mutation *AuthSessionMutation
}
// SetUserID sets the "user_id" field.
func (_u *AuthSessionUpdateOne) SetUserID(v string) *AuthSessionUpdateOne {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *AuthSessionUpdateOne) SetNillableUserID(v *string) *AuthSessionUpdateOne {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetConnectorID sets the "connector_id" field.
func (_u *AuthSessionUpdateOne) SetConnectorID(v string) *AuthSessionUpdateOne {
_u.mutation.SetConnectorID(v)
return _u
}
// SetNillableConnectorID sets the "connector_id" field if the given value is not nil.
func (_u *AuthSessionUpdateOne) SetNillableConnectorID(v *string) *AuthSessionUpdateOne {
if v != nil {
_u.SetConnectorID(*v)
}
return _u
}
// SetNonce sets the "nonce" field.
func (_u *AuthSessionUpdateOne) SetNonce(v string) *AuthSessionUpdateOne {
_u.mutation.SetNonce(v)
return _u
}
// SetNillableNonce sets the "nonce" field if the given value is not nil.
func (_u *AuthSessionUpdateOne) SetNillableNonce(v *string) *AuthSessionUpdateOne {
if v != nil {
_u.SetNonce(*v)
}
return _u
}
// SetClientStates sets the "client_states" field.
func (_u *AuthSessionUpdateOne) SetClientStates(v []byte) *AuthSessionUpdateOne {
_u.mutation.SetClientStates(v)
@@ -273,7 +389,30 @@ func (_u *AuthSessionUpdateOne) ExecX(ctx context.Context) {
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *AuthSessionUpdateOne) check() error {
if v, ok := _u.mutation.UserID(); ok {
if err := authsession.UserIDValidator(v); err != nil {
return &ValidationError{Name: "user_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.user_id": %w`, err)}
}
}
if v, ok := _u.mutation.ConnectorID(); ok {
if err := authsession.ConnectorIDValidator(v); err != nil {
return &ValidationError{Name: "connector_id", err: fmt.Errorf(`db: validator failed for field "AuthSession.connector_id": %w`, err)}
}
}
if v, ok := _u.mutation.Nonce(); ok {
if err := authsession.NonceValidator(v); err != nil {
return &ValidationError{Name: "nonce", err: fmt.Errorf(`db: validator failed for field "AuthSession.nonce": %w`, err)}
}
}
return nil
}
func (_u *AuthSessionUpdateOne) sqlSave(ctx context.Context) (_node *AuthSession, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(authsession.Table, authsession.Columns, sqlgraph.NewFieldSpec(authsession.FieldID, field.TypeString))
id, ok := _u.mutation.ID()
if !ok {
@@ -299,6 +438,15 @@ func (_u *AuthSessionUpdateOne) sqlSave(ctx context.Context) (_node *AuthSession
}
}
}
if value, ok := _u.mutation.UserID(); ok {
_spec.SetField(authsession.FieldUserID, field.TypeString, value)
}
if value, ok := _u.mutation.ConnectorID(); ok {
_spec.SetField(authsession.FieldConnectorID, field.TypeString, value)
}
if value, ok := _u.mutation.Nonce(); ok {
_spec.SetField(authsession.FieldNonce, field.TypeString, value)
}
if value, ok := _u.mutation.ClientStates(); ok {
_spec.SetField(authsession.FieldClientStates, field.TypeBytes, value)
}
+3
View File
@@ -66,6 +66,9 @@ var (
// AuthSessionsColumns holds the columns for the "auth_sessions" table.
AuthSessionsColumns = []*schema.Column{
{Name: "id", Type: field.TypeString, Unique: true, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}},
{Name: "user_id", Type: field.TypeString, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}},
{Name: "connector_id", Type: field.TypeString, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}},
{Name: "nonce", Type: field.TypeString, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}},
{Name: "client_states", Type: field.TypeBytes},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime(3)", "postgres": "timestamptz", "sqlite3": "timestamp"}},
{Name: "last_activity", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime(3)", "postgres": "timestamptz", "sqlite3": "timestamp"}},
+163 -1
View File
@@ -2727,6 +2727,9 @@ type AuthSessionMutation struct {
op Op
typ string
id *string
user_id *string
connector_id *string
nonce *string
client_states *[]byte
created_at *time.Time
last_activity *time.Time
@@ -2842,6 +2845,114 @@ func (m *AuthSessionMutation) IDs(ctx context.Context) ([]string, error) {
}
}
// SetUserID sets the "user_id" field.
func (m *AuthSessionMutation) SetUserID(s string) {
m.user_id = &s
}
// UserID returns the value of the "user_id" field in the mutation.
func (m *AuthSessionMutation) UserID() (r string, exists bool) {
v := m.user_id
if v == nil {
return
}
return *v, true
}
// OldUserID returns the old "user_id" field's value of the AuthSession entity.
// If the AuthSession object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AuthSessionMutation) OldUserID(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUserID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUserID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUserID: %w", err)
}
return oldValue.UserID, nil
}
// ResetUserID resets all changes to the "user_id" field.
func (m *AuthSessionMutation) ResetUserID() {
m.user_id = nil
}
// SetConnectorID sets the "connector_id" field.
func (m *AuthSessionMutation) SetConnectorID(s string) {
m.connector_id = &s
}
// ConnectorID returns the value of the "connector_id" field in the mutation.
func (m *AuthSessionMutation) ConnectorID() (r string, exists bool) {
v := m.connector_id
if v == nil {
return
}
return *v, true
}
// OldConnectorID returns the old "connector_id" field's value of the AuthSession entity.
// If the AuthSession object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AuthSessionMutation) OldConnectorID(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldConnectorID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldConnectorID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldConnectorID: %w", err)
}
return oldValue.ConnectorID, nil
}
// ResetConnectorID resets all changes to the "connector_id" field.
func (m *AuthSessionMutation) ResetConnectorID() {
m.connector_id = nil
}
// SetNonce sets the "nonce" field.
func (m *AuthSessionMutation) SetNonce(s string) {
m.nonce = &s
}
// Nonce returns the value of the "nonce" field in the mutation.
func (m *AuthSessionMutation) Nonce() (r string, exists bool) {
v := m.nonce
if v == nil {
return
}
return *v, true
}
// OldNonce returns the old "nonce" field's value of the AuthSession entity.
// If the AuthSession object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AuthSessionMutation) OldNonce(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldNonce is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldNonce requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldNonce: %w", err)
}
return oldValue.Nonce, nil
}
// ResetNonce resets all changes to the "nonce" field.
func (m *AuthSessionMutation) ResetNonce() {
m.nonce = nil
}
// SetClientStates sets the "client_states" field.
func (m *AuthSessionMutation) SetClientStates(b []byte) {
m.client_states = &b
@@ -3056,7 +3167,16 @@ func (m *AuthSessionMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *AuthSessionMutation) Fields() []string {
fields := make([]string, 0, 5)
fields := make([]string, 0, 8)
if m.user_id != nil {
fields = append(fields, authsession.FieldUserID)
}
if m.connector_id != nil {
fields = append(fields, authsession.FieldConnectorID)
}
if m.nonce != nil {
fields = append(fields, authsession.FieldNonce)
}
if m.client_states != nil {
fields = append(fields, authsession.FieldClientStates)
}
@@ -3080,6 +3200,12 @@ func (m *AuthSessionMutation) Fields() []string {
// schema.
func (m *AuthSessionMutation) Field(name string) (ent.Value, bool) {
switch name {
case authsession.FieldUserID:
return m.UserID()
case authsession.FieldConnectorID:
return m.ConnectorID()
case authsession.FieldNonce:
return m.Nonce()
case authsession.FieldClientStates:
return m.ClientStates()
case authsession.FieldCreatedAt:
@@ -3099,6 +3225,12 @@ func (m *AuthSessionMutation) Field(name string) (ent.Value, bool) {
// database failed.
func (m *AuthSessionMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case authsession.FieldUserID:
return m.OldUserID(ctx)
case authsession.FieldConnectorID:
return m.OldConnectorID(ctx)
case authsession.FieldNonce:
return m.OldNonce(ctx)
case authsession.FieldClientStates:
return m.OldClientStates(ctx)
case authsession.FieldCreatedAt:
@@ -3118,6 +3250,27 @@ func (m *AuthSessionMutation) OldField(ctx context.Context, name string) (ent.Va
// type.
func (m *AuthSessionMutation) SetField(name string, value ent.Value) error {
switch name {
case authsession.FieldUserID:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUserID(v)
return nil
case authsession.FieldConnectorID:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetConnectorID(v)
return nil
case authsession.FieldNonce:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetNonce(v)
return nil
case authsession.FieldClientStates:
v, ok := value.([]byte)
if !ok {
@@ -3202,6 +3355,15 @@ func (m *AuthSessionMutation) ClearField(name string) error {
// It returns an error if the field is not defined in the schema.
func (m *AuthSessionMutation) ResetField(name string) error {
switch name {
case authsession.FieldUserID:
m.ResetUserID()
return nil
case authsession.FieldConnectorID:
m.ResetConnectorID()
return nil
case authsession.FieldNonce:
m.ResetNonce()
return nil
case authsession.FieldClientStates:
m.ResetClientStates()
return nil
+14 -2
View File
@@ -90,12 +90,24 @@ func init() {
authrequest.IDValidator = authrequestDescID.Validators[0].(func(string) error)
authsessionFields := schema.AuthSession{}.Fields()
_ = authsessionFields
// authsessionDescUserID is the schema descriptor for user_id field.
authsessionDescUserID := authsessionFields[1].Descriptor()
// authsession.UserIDValidator is a validator for the "user_id" field. It is called by the builders before save.
authsession.UserIDValidator = authsessionDescUserID.Validators[0].(func(string) error)
// authsessionDescConnectorID is the schema descriptor for connector_id field.
authsessionDescConnectorID := authsessionFields[2].Descriptor()
// authsession.ConnectorIDValidator is a validator for the "connector_id" field. It is called by the builders before save.
authsession.ConnectorIDValidator = authsessionDescConnectorID.Validators[0].(func(string) error)
// authsessionDescNonce is the schema descriptor for nonce field.
authsessionDescNonce := authsessionFields[3].Descriptor()
// authsession.NonceValidator is a validator for the "nonce" field. It is called by the builders before save.
authsession.NonceValidator = authsessionDescNonce.Validators[0].(func(string) error)
// authsessionDescIPAddress is the schema descriptor for ip_address field.
authsessionDescIPAddress := authsessionFields[4].Descriptor()
authsessionDescIPAddress := authsessionFields[7].Descriptor()
// authsession.DefaultIPAddress holds the default value on creation for the ip_address field.
authsession.DefaultIPAddress = authsessionDescIPAddress.Default.(string)
// authsessionDescUserAgent is the schema descriptor for user_agent field.
authsessionDescUserAgent := authsessionFields[5].Descriptor()
authsessionDescUserAgent := authsessionFields[8].Descriptor()
// authsession.DefaultUserAgent holds the default value on creation for the user_agent field.
authsession.DefaultUserAgent = authsessionDescUserAgent.Default.(string)
// authsessionDescID is the schema descriptor for id field.

Some files were not shown because too many files have changed in this diff Show More