From ba3b047e179c2337c9ae82dc0da2e4480332f158 Mon Sep 17 00:00:00 2001 From: OscarLlamas6 Date: Wed, 25 Feb 2026 21:46:02 -0600 Subject: [PATCH 1/2] feat(iam): add user login tracking fields Add LastLoginPerProvider map and LastTokenIntrospection timestamp to User CR status. - LastLoginPerProvider: tracks last login timestamp per identity provider (github, google) Updated by auth provider on idpintent.succeeded events (actual IDP logins only) - LastTokenIntrospection: tracks last successful token validation timestamp Updated by authentication webhook on every token introspection (includes refreshes) Provides more frequent activity tracking than login events These fields enable the staff portal to display user login history and last activity. Closes: datum-cloud/staff-portal#282 --- .../crd/bases/iam/iam.miloapis.com_users.yaml | 18 ++++++++++++++++++ pkg/apis/iam/v1alpha1/user_types.go | 16 ++++++++++++++++ pkg/apis/iam/v1alpha1/zz_generated.deepcopy.go | 11 +++++++++++ 3 files changed, 45 insertions(+) diff --git a/config/crd/bases/iam/iam.miloapis.com_users.yaml b/config/crd/bases/iam/iam.miloapis.com_users.yaml index e548140f..f7794447 100644 --- a/config/crd/bases/iam/iam.miloapis.com_users.yaml +++ b/config/crd/bases/iam/iam.miloapis.com_users.yaml @@ -146,6 +146,16 @@ spec: - type type: object type: array + lastLoginPerProvider: + additionalProperties: + type: string + description: |- + LastLoginPerProvider tracks the most recent login timestamp for each identity provider + that the user has used to authenticate. The map key is the provider name (e.g., "github", "google") + and the value is the RFC3339 timestamp of the last successful login via that provider. + This field is updated by the auth provider when processing idpintent.succeeded events. + Note: This event is only triggered during actual IDP login, not on token refresh. + type: object lastLoginProvider: allOf: - enum: @@ -159,6 +169,14 @@ spec: user to log in (e.g., "github" or "google"). This field is set by the auth provider based on authentication events. type: string + lastTokenIntrospection: + description: |- + LastTokenIntrospection records the timestamp of the most recent successful token introspection + for this user. This is updated during authentication webhook calls when validating access tokens, + which occurs more frequently than actual IDP logins (including token refreshes). + The value is an RFC3339 timestamp. + format: date-time + type: string registrationApproval: description: |- RegistrationApproval represents the administrator’s decision on the user’s registration request. diff --git a/pkg/apis/iam/v1alpha1/user_types.go b/pkg/apis/iam/v1alpha1/user_types.go index 64825f69..32d2e234 100644 --- a/pkg/apis/iam/v1alpha1/user_types.go +++ b/pkg/apis/iam/v1alpha1/user_types.go @@ -106,6 +106,22 @@ type UserStatus struct { // +kubebuilder:validation:Enum=github;google LastLoginProvider AuthProvider `json:"lastLoginProvider,omitempty"` + // LastLoginPerProvider tracks the most recent login timestamp for each identity provider + // that the user has used to authenticate. The map key is the provider name (e.g., "github", "google") + // and the value is the RFC3339 timestamp of the last successful login via that provider. + // This field is updated by the auth provider when processing idpintent.succeeded events. + // Note: This event is only triggered during actual IDP login, not on token refresh. + // +kubebuilder:validation:Optional + LastLoginPerProvider map[string]string `json:"lastLoginPerProvider,omitempty"` + + // LastTokenIntrospection records the timestamp of the most recent successful token introspection + // for this user. This is updated during authentication webhook calls when validating access tokens, + // which occurs more frequently than actual IDP logins (including token refreshes). + // The value is an RFC3339 timestamp. + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Format=date-time + LastTokenIntrospection *metav1.Time `json:"lastTokenIntrospection,omitempty"` + // AvatarURL points to the avatar image associated with the user. This value is // populated by the auth provider or any service that provides a user avatar URL. // +kubebuilder:validation:Optional diff --git a/pkg/apis/iam/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/iam/v1alpha1/zz_generated.deepcopy.go index 0218d4c2..e99cbfcd 100644 --- a/pkg/apis/iam/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/iam/v1alpha1/zz_generated.deepcopy.go @@ -1596,6 +1596,17 @@ func (in *UserStatus) DeepCopyInto(out *UserStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.LastLoginPerProvider != nil { + in, out := &in.LastLoginPerProvider, &out.LastLoginPerProvider + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.LastTokenIntrospection != nil { + in, out := &in.LastTokenIntrospection, &out.LastTokenIntrospection + *out = (*in).DeepCopy() + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus. From 2196fd50add047cb3d94b7ddd39e6ade00e6d675 Mon Sep 17 00:00:00 2001 From: OscarLlamas6 Date: Wed, 25 Feb 2026 22:06:59 -0600 Subject: [PATCH 2/2] docs: update User API documentation with new login tracking fields Add documentation for lastLoginPerProvider and lastTokenIntrospection status fields in the User resource API reference. --- docs/api/iam.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/api/iam.md b/docs/api/iam.md index 1ee17986..2f3a312f 100644 --- a/docs/api/iam.md +++ b/docs/api/iam.md @@ -3827,6 +3827,17 @@ populated by the auth provider or any service that provides a user avatar URL.Default: [map[lastTransitionTime:1970-01-01T00:00:00Z message:Waiting for control plane to reconcile reason:Unknown status:Unknown type:Ready]]
false + + lastLoginPerProvider + map[string]string + + LastLoginPerProvider tracks the most recent login timestamp for each identity provider +that the user has used to authenticate. The map key is the provider name (e.g., "github", "google") +and the value is the RFC3339 timestamp of the last successful login via that provider. +This field is updated by the auth provider when processing idpintent.succeeded events. +Note: This event is only triggered during actual IDP login, not on token refresh.
+ + false lastLoginProvider string @@ -3836,11 +3847,23 @@ user to log in (e.g., "github" or "google"). This field is set by the auth provi based on authentication events.
false + + lastTokenIntrospection + string + + LastTokenIntrospection records the timestamp of the most recent successful token introspection +for this user. This is updated during authentication webhook calls when validating access tokens, +which occurs more frequently than actual IDP logins (including token refreshes). +The value is an RFC3339 timestamp.
+
+ Format: date-time
+ + false registrationApproval enum - RegistrationApproval represents the administrator’s decision on the user’s registration request. + RegistrationApproval represents the administrator's decision on the user's registration request. States: - Pending: The user is awaiting review by an administrator. - Approved: The user registration has been approved.