use super::OptionValueExt;
#[macro_export]
macro_rules! auth_catalog_machine_dsl {
($rust_crate:literal, $rust_module:literal) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CredentialUseIntent {
UseCredential,
HoldAuthority,
BeginRefresh,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CredentialUseDisposition {
Authorized,
RefreshRequired,
RefreshDisallowed,
ReauthRequired,
LeaseAbsent,
AlreadyRefreshing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefreshFailureDisposition {
Transient,
ReauthRequired,
}
meerkat_machine_dsl::machine! {
machine AuthMachine {
version: 1,
rust: $rust_crate / $rust_module,
state {
lifecycle_phase: AuthLifecyclePhase,
expires_at: Option<u64>,
last_refresh: Option<u64>,
refresh_attempt: u64,
credential_present: bool,
credential_generation: u64,
credential_published_at_millis: Option<u64>,
oauth_browser_flow_ids: Set<String>,
oauth_browser_flow_providers: Map<String, String>,
oauth_browser_flow_redirect_uris: Map<String, String>,
oauth_browser_flow_expires_at_millis: Map<String, u64>,
oauth_device_flow_ids: Set<String>,
oauth_device_flow_providers: Map<String, String>,
oauth_device_flow_expires_at_millis: Map<String, u64>,
oauth_device_poll_ids: Set<String>,
oauth_outstanding_flow_count: u64,
release_draining: bool,
}
init(Valid) {
expires_at = None,
last_refresh = None,
refresh_attempt = 0,
credential_present = false,
credential_generation = 0,
credential_published_at_millis = None,
oauth_browser_flow_ids = EmptySet,
oauth_browser_flow_providers = EmptyMap,
oauth_browser_flow_redirect_uris = EmptyMap,
oauth_browser_flow_expires_at_millis = EmptyMap,
oauth_device_flow_ids = EmptySet,
oauth_device_flow_providers = EmptyMap,
oauth_device_flow_expires_at_millis = EmptyMap,
oauth_device_poll_ids = EmptySet,
oauth_outstanding_flow_count = 0,
release_draining = false,
}
terminal [Released]
phase AuthLifecyclePhase {
Valid,
Expiring,
Expired,
Refreshing,
ReauthRequired,
Released,
}
input AuthMachineInput {
Acquire { expires_at_ts: Option<u64>, credential_published_at_millis: u64 },
MarkExpiring,
ObserveCredentialFreshness { now_ts: u64, refresh_window_secs: u64 },
BeginRefresh,
CompleteRefresh { new_expires_at: Option<u64>, now_ts: u64, credential_published_at_millis: u64 },
RefreshFailed { disposition: Enum<RefreshFailureDisposition> },
MarkReauthRequired,
ClearCredentialLifecycle,
ReleaseCredentialLifecycle,
BeginRelease,
Release,
RestoreAuthoritySnapshot {
lifecycle_phase: AuthLifecyclePhase,
expires_at: Option<u64>,
last_refresh: Option<u64>,
refresh_attempt: u64,
credential_present: bool,
credential_generation: u64,
credential_published_at_millis: Option<u64>,
},
RestoreCredentialLifecycleSnapshot {
lifecycle_phase: Option<Enum<AuthLifecyclePhase>>,
expires_at: Option<u64>,
last_refresh: Option<u64>,
refresh_attempt: u64,
credential_present: bool,
credential_generation: u64,
credential_published_at_millis: Option<u64>,
restored_oauth_membership_observed: bool,
},
RestoreOAuthBrowserFlow { flow_id: String, provider: Option<String>, redirect_uri: Option<String>, expires_at_millis: Option<u64> },
RestoreOAuthDeviceFlow { flow_id: String, provider: Option<String>, expires_at_millis: Option<u64> },
RestoreOAuthDevicePoll { flow_id: String },
AdmitOAuthBrowserFlow {
flow_id: String,
provider: String,
redirect_uri: String,
expires_at_millis: u64,
max_outstanding_flows: u64,
observed_global_outstanding_flows: u64,
},
VerifyOAuthBrowserFlow { flow_id: String, provider: String, redirect_uri: String, now_millis: u64 },
ConsumeOAuthBrowserFlow { flow_id: String, provider: String, redirect_uri: String, now_millis: u64 },
ExpireOAuthBrowserFlow { flow_id: String },
AdmitOAuthDeviceFlow {
flow_id: String,
provider: String,
expires_at_millis: u64,
max_outstanding_flows: u64,
observed_global_outstanding_flows: u64,
},
ConfirmOAuthDurableAdmission {
observed_global_outstanding_flows: u64,
max_outstanding_flows: u64,
},
VerifyOAuthDeviceFlow { flow_id: String, provider: String, now_millis: u64 },
BeginOAuthDevicePoll { flow_id: String, provider: String, now_millis: u64 },
FinishOAuthDevicePoll { flow_id: String },
ConsumeOAuthDeviceFlow { flow_id: String, provider: String, now_millis: u64 },
ExpireOAuthDeviceFlow { flow_id: String },
ResolveRefreshFailureDisposition {
http_status: Option<u64>,
oauth_error_code: Option<String>,
local_credential_unusable: bool,
},
ResolveCredentialUseAdmission { intent: Enum<CredentialUseIntent> },
ResolveOAuthLoginCredentialDisposition {
credential_present: bool,
force_refresh: bool,
refresh_allowed: bool,
},
}
effect AuthMachineEffect {
EmitLifecycleEvent {
new_state: AuthLifecyclePhase,
expires_at: Option<u64>,
credential_generation: u64,
credential_published_at_millis: Option<u64>,
},
WakeRefreshLoop,
RefreshFailureDispositionResolved { disposition: Enum<RefreshFailureDisposition> },
CredentialUseAdmissionResolved { disposition: Enum<CredentialUseDisposition> },
CancelOAuthFlowsForRelease {
browser_flow_ids: Set<String>,
device_flow_ids: Set<String>,
},
}
disposition EmitLifecycleEvent => external handoff auth_lease_lifecycle_publication seam SurfaceResultAlignment,
disposition WakeRefreshLoop => local seam NoOwnerRealization,
disposition RefreshFailureDispositionResolved => local seam SurfaceResultAlignment,
disposition CredentialUseAdmissionResolved => local seam SurfaceResultAlignment,
disposition CancelOAuthFlowsForRelease => external handoff auth_release_oauth_flow_drain seam NoOwnerRealization,
invariant oauth_flow_membership_consistent {
self.oauth_browser_flow_providers.keys() == self.oauth_browser_flow_ids
&& self.oauth_browser_flow_redirect_uris.keys() == self.oauth_browser_flow_ids
&& self.oauth_browser_flow_expires_at_millis.keys() == self.oauth_browser_flow_ids
&& self.oauth_device_flow_providers.keys() == self.oauth_device_flow_ids
&& self.oauth_device_flow_expires_at_millis.keys() == self.oauth_device_flow_ids
&& for_all(flow_id in self.oauth_device_poll_ids, self.oauth_device_flow_ids.contains(flow_id))
&& self.oauth_outstanding_flow_count == self.oauth_browser_flow_ids.len() + self.oauth_device_flow_ids.len()
}
invariant released_oauth_membership_drained {
self.lifecycle_phase != Phase::Released || self.oauth_outstanding_flow_count == 0
}
invariant released_not_release_draining {
self.lifecycle_phase != Phase::Released || self.release_draining == false
}
transition Acquire {
on input Acquire { expires_at_ts, credential_published_at_millis }
update {
self.expires_at = expires_at_ts;
self.refresh_attempt = 0;
self.credential_present = true;
self.credential_generation = self.credential_generation + 1;
self.credential_published_at_millis = Some(credential_published_at_millis);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition MarkExpiring {
on input MarkExpiring
guard { self.lifecycle_phase == Phase::Valid }
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessValid {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Valid }
guard "credential_not_in_refresh_window" {
if self.expires_at == None {
true
} else {
now_ts + refresh_window_secs <= self.expires_at.get("value")
}
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessExpiringFromValid {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Valid }
guard "credential_in_refresh_window" {
if self.expires_at == None {
false
} else {
now_ts < self.expires_at.get("value")
&& self.expires_at.get("value") < now_ts + refresh_window_secs
}
}
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessExpiredFromValid {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Valid }
guard "credential_expired" {
if self.expires_at == None {
false
} else {
self.expires_at.get("value") <= now_ts
}
}
to Expired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessExpiring {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Expiring }
guard "credential_not_expired" {
if self.expires_at == None {
true
} else {
now_ts < self.expires_at.get("value")
}
}
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessExpiredFromExpiring {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Expiring }
guard "credential_expired" {
if self.expires_at == None {
false
} else {
self.expires_at.get("value") <= now_ts
}
}
to Expired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessExpired {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Expired }
to Expired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessRefreshing {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Refreshing }
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessReauthRequired {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::ReauthRequired }
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ObserveCredentialFreshnessReleased {
on input ObserveCredentialFreshness { now_ts, refresh_window_secs }
guard { self.lifecycle_phase == Phase::Released }
to Released
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition BeginRefreshFromValid {
on input BeginRefresh
guard { self.lifecycle_phase == Phase::Valid }
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
emit WakeRefreshLoop
}
transition BeginRefreshFromExpiring {
on input BeginRefresh
guard { self.lifecycle_phase == Phase::Expiring }
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
emit WakeRefreshLoop
}
transition BeginRefreshFromExpired {
on input BeginRefresh
guard { self.lifecycle_phase == Phase::Expired }
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
emit WakeRefreshLoop
}
transition CompleteRefresh {
on input CompleteRefresh { new_expires_at, now_ts, credential_published_at_millis }
guard { self.lifecycle_phase == Phase::Refreshing }
guard "new_credential_not_expired" {
if new_expires_at == None {
true
} else {
now_ts < new_expires_at.get("value")
}
}
update {
self.expires_at = new_expires_at;
self.last_refresh = Some(now_ts);
self.refresh_attempt = 0;
self.credential_present = true;
self.credential_generation = self.credential_generation + 1;
self.credential_published_at_millis = Some(credential_published_at_millis);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ResolveRefreshFailureDispositionTransient {
per_phase [Refreshing]
on input ResolveRefreshFailureDisposition { http_status, oauth_error_code, local_credential_unusable }
guard "refresh_failure_observation_transient" {
local_credential_unusable == false
&& http_status != Some(401)
&& http_status != Some(403)
&& oauth_error_code != Some("invalid_grant")
&& oauth_error_code != Some("invalid_client")
&& oauth_error_code != Some("unauthorized_client")
&& oauth_error_code != Some("invalid_scope")
&& oauth_error_code != Some("access_denied")
&& oauth_error_code != Some("permission_denied")
&& oauth_error_code != Some("expired_token")
}
update {}
to Refreshing
emit RefreshFailureDispositionResolved {
disposition: RefreshFailureDisposition::Transient,
}
}
transition ResolveRefreshFailureDispositionPermanent {
per_phase [Refreshing]
on input ResolveRefreshFailureDisposition { http_status, oauth_error_code, local_credential_unusable }
guard "refresh_failure_observation_permanent" {
local_credential_unusable == true
|| http_status == Some(401)
|| http_status == Some(403)
|| oauth_error_code == Some("invalid_grant")
|| oauth_error_code == Some("invalid_client")
|| oauth_error_code == Some("unauthorized_client")
|| oauth_error_code == Some("invalid_scope")
|| oauth_error_code == Some("access_denied")
|| oauth_error_code == Some("permission_denied")
|| oauth_error_code == Some("expired_token")
}
update {}
to Refreshing
emit RefreshFailureDispositionResolved {
disposition: RefreshFailureDisposition::ReauthRequired,
}
}
transition RefreshFailedTransient {
on input RefreshFailed { disposition }
guard { self.lifecycle_phase == Phase::Refreshing }
guard "refresh_failure_disposition_transient" {
disposition == RefreshFailureDisposition::Transient
}
update {
self.refresh_attempt = self.refresh_attempt + 1;
}
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RefreshFailedPermanent {
on input RefreshFailed { disposition }
guard { self.lifecycle_phase == Phase::Refreshing }
guard "refresh_failure_disposition_reauth_required" {
disposition == RefreshFailureDisposition::ReauthRequired
}
update {
self.refresh_attempt = self.refresh_attempt + 1;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition MarkReauthRequiredFromValid {
on input MarkReauthRequired
guard { self.lifecycle_phase == Phase::Valid }
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition MarkReauthRequiredFromExpiring {
on input MarkReauthRequired
guard { self.lifecycle_phase == Phase::Expiring }
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition MarkReauthRequiredFromExpired {
on input MarkReauthRequired
guard { self.lifecycle_phase == Phase::Expired }
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition MarkReauthRequiredFromRefreshing {
on input MarkReauthRequired
guard { self.lifecycle_phase == Phase::Refreshing }
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ClearCredentialLifecycle {
on input ClearCredentialLifecycle
update {
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
self.credential_published_at_millis = None;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ReleaseCredentialLifecycleWithOAuth {
on input ReleaseCredentialLifecycle
guard "oauth_membership_present" { self.oauth_outstanding_flow_count > 0 }
update {
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
self.credential_published_at_millis = None;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ReleaseCredentialLifecycleWithoutOAuth {
on input ReleaseCredentialLifecycle
guard "oauth_membership_absent" { self.oauth_outstanding_flow_count == 0 }
update {
self.release_draining = false;
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
self.credential_published_at_millis = None;
self.oauth_browser_flow_ids = EmptySet;
self.oauth_browser_flow_providers = EmptyMap;
self.oauth_browser_flow_redirect_uris = EmptyMap;
self.oauth_browser_flow_expires_at_millis = EmptyMap;
self.oauth_device_flow_ids = EmptySet;
self.oauth_device_flow_providers = EmptyMap;
self.oauth_device_flow_expires_at_millis = EmptyMap;
self.oauth_device_poll_ids = EmptySet;
self.oauth_outstanding_flow_count = 0;
}
to Released
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition BeginReleaseDrainingOAuthFlows {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input BeginRelease
guard "oauth_membership_present" { self.oauth_outstanding_flow_count > 0 }
update {
self.release_draining = true;
}
to Valid
emit CancelOAuthFlowsForRelease {
browser_flow_ids: self.oauth_browser_flow_ids,
device_flow_ids: self.oauth_device_flow_ids,
}
}
transition BeginReleaseWithoutOAuthFlows {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input BeginRelease
guard "oauth_membership_absent" { self.oauth_outstanding_flow_count == 0 }
update {
self.release_draining = true;
}
to Valid
}
transition BeginReleaseReleased {
on input BeginRelease
guard { self.lifecycle_phase == Phase::Released }
update {}
to Released
}
transition Release {
on input Release
guard "oauth_release_drained" { self.oauth_outstanding_flow_count == 0 }
update {
self.release_draining = false;
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
self.credential_published_at_millis = None;
self.oauth_browser_flow_ids = EmptySet;
self.oauth_browser_flow_providers = EmptyMap;
self.oauth_browser_flow_redirect_uris = EmptyMap;
self.oauth_browser_flow_expires_at_millis = EmptyMap;
self.oauth_device_flow_ids = EmptySet;
self.oauth_device_flow_providers = EmptyMap;
self.oauth_device_flow_expires_at_millis = EmptyMap;
self.oauth_device_poll_ids = EmptySet;
self.oauth_outstanding_flow_count = 0;
}
to Released
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotValid {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard { lifecycle_phase == Some(AuthLifecyclePhase::Valid) && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotExpiring {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard { lifecycle_phase == Some(AuthLifecyclePhase::Expiring) && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotRefreshing {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard { lifecycle_phase == Some(AuthLifecyclePhase::Refreshing) && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotExpired {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard { lifecycle_phase == Some(AuthLifecyclePhase::Expired) && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Expired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotReauthRequired {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard { lifecycle_phase == Some(AuthLifecyclePhase::ReauthRequired) && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotNoCredentialWithOAuth {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard "restore_snapshot_has_no_credential" { credential_present == false || lifecycle_phase == None || lifecycle_phase == Some(AuthLifecyclePhase::Released) }
guard "restore_oauth_membership_present" { self.oauth_outstanding_flow_count > 0 || restored_oauth_membership_observed }
update {
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = None;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreCredentialLifecycleSnapshotNoCredentialWithoutOAuth {
on input RestoreCredentialLifecycleSnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis,
restored_oauth_membership_observed
}
guard "restore_snapshot_has_no_credential" { credential_present == false || lifecycle_phase == None || lifecycle_phase == Some(AuthLifecyclePhase::Released) }
guard "restore_oauth_membership_absent" { self.oauth_outstanding_flow_count == 0 && restored_oauth_membership_observed == false }
update {
self.release_draining = false;
self.expires_at = None;
self.last_refresh = None;
self.refresh_attempt = 0;
self.credential_present = false;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = None;
self.oauth_browser_flow_ids = EmptySet;
self.oauth_browser_flow_providers = EmptyMap;
self.oauth_browser_flow_redirect_uris = EmptyMap;
self.oauth_browser_flow_expires_at_millis = EmptyMap;
self.oauth_device_flow_ids = EmptySet;
self.oauth_device_flow_providers = EmptyMap;
self.oauth_device_flow_expires_at_millis = EmptyMap;
self.oauth_device_poll_ids = EmptySet;
self.oauth_outstanding_flow_count = 0;
}
to Released
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotValid {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::Valid && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotExpiring {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::Expiring && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Expiring
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotRefreshing {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::Refreshing && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Refreshing
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotExpired {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::Expired && credential_present && credential_published_at_millis != None }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Expired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotReauthRequired {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::ReauthRequired && (credential_present == false || credential_published_at_millis != None) }
update {
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreAuthoritySnapshotReleased {
on input RestoreAuthoritySnapshot {
lifecycle_phase,
expires_at,
last_refresh,
refresh_attempt,
credential_present,
credential_generation,
credential_published_at_millis
}
guard { lifecycle_phase == Phase::Released && credential_present == false && credential_published_at_millis == None && self.oauth_outstanding_flow_count == 0 }
update {
self.release_draining = false;
self.expires_at = expires_at;
self.last_refresh = last_refresh;
self.refresh_attempt = refresh_attempt;
self.credential_present = credential_present;
if credential_generation > self.credential_generation {
self.credential_generation = credential_generation;
}
self.credential_published_at_millis = credential_published_at_millis;
}
to Released
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreOAuthBrowserFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input RestoreOAuthBrowserFlow { flow_id, provider, redirect_uri, expires_at_millis }
guard "not_release_draining" { self.release_draining == false }
guard "browser_restore_has_provider" { provider != None }
guard "browser_restore_has_redirect_uri" { redirect_uri != None }
guard "browser_restore_has_expiry" { expires_at_millis != None }
update {
if self.oauth_browser_flow_ids.contains(flow_id) == false {
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
self.oauth_browser_flow_ids.insert(flow_id);
self.oauth_browser_flow_providers.insert(flow_id, provider.get("value"));
self.oauth_browser_flow_redirect_uris.insert(flow_id, redirect_uri.get("value"));
self.oauth_browser_flow_expires_at_millis.insert(flow_id, expires_at_millis.get("value"));
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreOAuthDeviceFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input RestoreOAuthDeviceFlow { flow_id, provider, expires_at_millis }
guard "not_release_draining" { self.release_draining == false }
guard "device_restore_has_provider" { provider != None }
guard "device_restore_has_expiry" { expires_at_millis != None }
update {
if self.oauth_device_flow_ids.contains(flow_id) == false {
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
self.oauth_device_flow_ids.insert(flow_id);
self.oauth_device_flow_providers.insert(flow_id, provider.get("value"));
self.oauth_device_flow_expires_at_millis.insert(flow_id, expires_at_millis.get("value"));
self.oauth_device_poll_ids.remove(flow_id);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition RestoreOAuthDevicePoll {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input RestoreOAuthDevicePoll { flow_id }
guard "not_release_draining" { self.release_draining == false }
guard "device_flow_present_for_poll_restore" { self.oauth_device_flow_ids.contains(flow_id) }
update {
self.oauth_device_poll_ids.insert(flow_id);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition AdmitOAuthBrowserFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input AdmitOAuthBrowserFlow { flow_id, provider, redirect_uri, expires_at_millis, max_outstanding_flows, observed_global_outstanding_flows }
guard "not_release_draining" { self.release_draining == false }
guard "browser_flow_absent" { self.oauth_browser_flow_ids.contains(flow_id) == false }
guard "oauth_capacity_available" { self.oauth_outstanding_flow_count < max_outstanding_flows }
guard "oauth_global_capacity_available" { observed_global_outstanding_flows < max_outstanding_flows }
update {
self.oauth_browser_flow_ids.insert(flow_id);
self.oauth_browser_flow_providers.insert(flow_id, provider);
self.oauth_browser_flow_redirect_uris.insert(flow_id, redirect_uri);
self.oauth_browser_flow_expires_at_millis.insert(flow_id, expires_at_millis);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ReopenReleasedForOAuthBrowserFlowAdmission {
on input AdmitOAuthBrowserFlow { flow_id, provider, redirect_uri, expires_at_millis, max_outstanding_flows, observed_global_outstanding_flows }
guard { self.lifecycle_phase == Phase::Released }
guard "released_without_credential" { self.credential_present == false && self.credential_published_at_millis == None }
guard "released_without_oauth_membership" { self.oauth_outstanding_flow_count == 0 }
guard "oauth_capacity_available" { self.oauth_outstanding_flow_count < max_outstanding_flows }
guard "oauth_global_capacity_available" { observed_global_outstanding_flows < max_outstanding_flows }
update {
self.oauth_browser_flow_ids.insert(flow_id);
self.oauth_browser_flow_providers.insert(flow_id, provider);
self.oauth_browser_flow_redirect_uris.insert(flow_id, redirect_uri);
self.oauth_browser_flow_expires_at_millis.insert(flow_id, expires_at_millis);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition VerifyOAuthBrowserFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input VerifyOAuthBrowserFlow { flow_id, provider, redirect_uri, now_millis }
guard "browser_flow_present" { self.oauth_browser_flow_ids.contains(flow_id) }
guard "browser_flow_provider_matches" { self.oauth_browser_flow_providers.get_cloned(flow_id) == Some(provider) }
guard "browser_flow_redirect_uri_matches" { self.oauth_browser_flow_redirect_uris.get_cloned(flow_id) == Some(redirect_uri) }
guard "browser_flow_not_expired" { now_millis <= self.oauth_browser_flow_expires_at_millis.get_cloned(flow_id).get("value") }
update {}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ConsumeOAuthBrowserFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ConsumeOAuthBrowserFlow { flow_id, provider, redirect_uri, now_millis }
guard "browser_flow_present" { self.oauth_browser_flow_ids.contains(flow_id) }
guard "browser_flow_provider_matches" { self.oauth_browser_flow_providers.get_cloned(flow_id) == Some(provider) }
guard "browser_flow_redirect_uri_matches" { self.oauth_browser_flow_redirect_uris.get_cloned(flow_id) == Some(redirect_uri) }
guard "browser_flow_not_expired" { now_millis <= self.oauth_browser_flow_expires_at_millis.get_cloned(flow_id).get("value") }
update {
self.oauth_browser_flow_ids.remove(flow_id);
self.oauth_browser_flow_providers.remove(flow_id);
self.oauth_browser_flow_redirect_uris.remove(flow_id);
self.oauth_browser_flow_expires_at_millis.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count - 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ExpireOAuthBrowserFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ExpireOAuthBrowserFlow { flow_id }
guard "browser_flow_present" { self.oauth_browser_flow_ids.contains(flow_id) }
update {
self.oauth_browser_flow_ids.remove(flow_id);
self.oauth_browser_flow_providers.remove(flow_id);
self.oauth_browser_flow_redirect_uris.remove(flow_id);
self.oauth_browser_flow_expires_at_millis.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count - 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ExpireOAuthBrowserFlowAbsent {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ExpireOAuthBrowserFlow { flow_id }
guard "browser_flow_absent" { self.oauth_browser_flow_ids.contains(flow_id) == false }
update {}
to Valid
}
transition ExpireOAuthBrowserFlowReleased {
on input ExpireOAuthBrowserFlow { flow_id }
guard { self.lifecycle_phase == Phase::Released }
update {}
to Released
}
transition AdmitOAuthDeviceFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input AdmitOAuthDeviceFlow { flow_id, provider, expires_at_millis, max_outstanding_flows, observed_global_outstanding_flows }
guard "not_release_draining" { self.release_draining == false }
guard "device_flow_absent" { self.oauth_device_flow_ids.contains(flow_id) == false }
guard "oauth_capacity_available" { self.oauth_outstanding_flow_count < max_outstanding_flows }
guard "oauth_global_capacity_available" { observed_global_outstanding_flows < max_outstanding_flows }
update {
self.oauth_device_flow_ids.insert(flow_id);
self.oauth_device_flow_providers.insert(flow_id, provider);
self.oauth_device_flow_expires_at_millis.insert(flow_id, expires_at_millis);
self.oauth_device_poll_ids.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ReopenReleasedForOAuthDeviceFlowAdmission {
on input AdmitOAuthDeviceFlow { flow_id, provider, expires_at_millis, max_outstanding_flows, observed_global_outstanding_flows }
guard { self.lifecycle_phase == Phase::Released }
guard "released_without_credential" { self.credential_present == false && self.credential_published_at_millis == None }
guard "released_without_oauth_membership" { self.oauth_outstanding_flow_count == 0 }
guard "oauth_capacity_available" { self.oauth_outstanding_flow_count < max_outstanding_flows }
guard "oauth_global_capacity_available" { observed_global_outstanding_flows < max_outstanding_flows }
update {
self.oauth_device_flow_ids.insert(flow_id);
self.oauth_device_flow_providers.insert(flow_id, provider);
self.oauth_device_flow_expires_at_millis.insert(flow_id, expires_at_millis);
self.oauth_device_poll_ids.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count + 1;
}
to ReauthRequired
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ConfirmOAuthDurableAdmission {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ConfirmOAuthDurableAdmission { observed_global_outstanding_flows, max_outstanding_flows }
guard "oauth_global_capacity_available" { observed_global_outstanding_flows < max_outstanding_flows }
update {}
to Valid
}
transition ConfirmOAuthDurableAdmissionReleased {
on input ConfirmOAuthDurableAdmission { observed_global_outstanding_flows, max_outstanding_flows }
guard { self.lifecycle_phase == Phase::Released }
update {}
to Released
}
transition VerifyOAuthDeviceFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input VerifyOAuthDeviceFlow { flow_id, provider, now_millis }
guard "device_flow_present" { self.oauth_device_flow_ids.contains(flow_id) }
guard "device_flow_provider_matches" { self.oauth_device_flow_providers.get_cloned(flow_id) == Some(provider) }
guard "device_flow_not_expired" { now_millis <= self.oauth_device_flow_expires_at_millis.get_cloned(flow_id).get("value") }
update {}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition BeginOAuthDevicePoll {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input BeginOAuthDevicePoll { flow_id, provider, now_millis }
guard "device_flow_present" { self.oauth_device_flow_ids.contains(flow_id) }
guard "device_flow_provider_matches" { self.oauth_device_flow_providers.get_cloned(flow_id) == Some(provider) }
guard "device_flow_not_expired" { now_millis <= self.oauth_device_flow_expires_at_millis.get_cloned(flow_id).get("value") }
guard "device_poll_absent" { self.oauth_device_poll_ids.contains(flow_id) == false }
update {
self.oauth_device_poll_ids.insert(flow_id);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition FinishOAuthDevicePoll {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input FinishOAuthDevicePoll { flow_id }
guard "device_poll_present" { self.oauth_device_poll_ids.contains(flow_id) }
update {
self.oauth_device_poll_ids.remove(flow_id);
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition FinishOAuthDevicePollAbsent {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input FinishOAuthDevicePoll { flow_id }
guard "device_poll_absent" { self.oauth_device_poll_ids.contains(flow_id) == false }
update {}
to Valid
}
transition FinishOAuthDevicePollReleased {
on input FinishOAuthDevicePoll { flow_id }
guard { self.lifecycle_phase == Phase::Released }
update {}
to Released
}
transition ConsumeOAuthDeviceFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ConsumeOAuthDeviceFlow { flow_id, provider, now_millis }
guard "device_flow_present" { self.oauth_device_flow_ids.contains(flow_id) }
guard "device_flow_provider_matches" { self.oauth_device_flow_providers.get_cloned(flow_id) == Some(provider) }
guard "device_flow_not_expired" { now_millis <= self.oauth_device_flow_expires_at_millis.get_cloned(flow_id).get("value") }
update {
self.oauth_device_flow_ids.remove(flow_id);
self.oauth_device_flow_providers.remove(flow_id);
self.oauth_device_flow_expires_at_millis.remove(flow_id);
self.oauth_device_poll_ids.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count - 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ExpireOAuthDeviceFlow {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ExpireOAuthDeviceFlow { flow_id }
guard "device_flow_present" { self.oauth_device_flow_ids.contains(flow_id) }
update {
self.oauth_device_flow_ids.remove(flow_id);
self.oauth_device_flow_providers.remove(flow_id);
self.oauth_device_flow_expires_at_millis.remove(flow_id);
self.oauth_device_poll_ids.remove(flow_id);
self.oauth_outstanding_flow_count = self.oauth_outstanding_flow_count - 1;
}
to Valid
emit EmitLifecycleEvent {
new_state: self.lifecycle_phase,
expires_at: self.expires_at,
credential_generation: self.credential_generation,
credential_published_at_millis: self.credential_published_at_millis,
}
}
transition ExpireOAuthDeviceFlowAbsent {
per_phase [Valid, Expiring, Expired, Refreshing, ReauthRequired]
on input ExpireOAuthDeviceFlow { flow_id }
guard "device_flow_absent" { self.oauth_device_flow_ids.contains(flow_id) == false }
update {}
to Valid
}
transition ExpireOAuthDeviceFlowReleased {
on input ExpireOAuthDeviceFlow { flow_id }
guard { self.lifecycle_phase == Phase::Released }
update {}
to Released
}
transition ResolveCredentialUseAdmissionValidUseAuthorized {
per_phase [Valid]
on input ResolveCredentialUseAdmission { intent }
guard "valid_use_authorized" {
intent == CredentialUseIntent::UseCredential && self.credential_present
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::Authorized }
}
transition ResolveCredentialUseAdmissionValidHoldAuthorized {
per_phase [Valid]
on input ResolveCredentialUseAdmission { intent }
guard "valid_hold_authorized" {
intent == CredentialUseIntent::HoldAuthority && self.credential_present
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::Authorized }
}
transition ResolveCredentialUseAdmissionValidBeginRefresh {
per_phase [Valid]
on input ResolveCredentialUseAdmission { intent }
guard "valid_begin_refresh" {
intent == CredentialUseIntent::BeginRefresh && self.credential_present
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionValidNoCredential {
per_phase [Valid]
on input ResolveCredentialUseAdmission { intent }
guard "valid_no_credential" { self.credential_present == false }
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::LeaseAbsent }
}
transition ResolveCredentialUseAdmissionExpiringUseRefresh {
per_phase [Expiring]
on input ResolveCredentialUseAdmission { intent }
guard "expiring_use_refresh" {
intent == CredentialUseIntent::UseCredential && self.credential_present
}
update {}
to Expiring
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionExpiringHoldAuthorized {
per_phase [Expiring]
on input ResolveCredentialUseAdmission { intent }
guard "expiring_hold_authorized" {
intent == CredentialUseIntent::HoldAuthority && self.credential_present
}
update {}
to Expiring
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::Authorized }
}
transition ResolveCredentialUseAdmissionExpiringBeginRefresh {
per_phase [Expiring]
on input ResolveCredentialUseAdmission { intent }
guard "expiring_begin_refresh" {
intent == CredentialUseIntent::BeginRefresh && self.credential_present
}
update {}
to Expiring
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionExpiringNoCredential {
per_phase [Expiring]
on input ResolveCredentialUseAdmission { intent }
guard "expiring_no_credential" { self.credential_present == false }
update {}
to Expiring
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::LeaseAbsent }
}
transition ResolveCredentialUseAdmissionExpiredUseRefresh {
per_phase [Expired]
on input ResolveCredentialUseAdmission { intent }
guard "expired_use_refresh" {
intent == CredentialUseIntent::UseCredential && self.credential_present
}
update {}
to Expired
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionExpiredHoldRefresh {
per_phase [Expired]
on input ResolveCredentialUseAdmission { intent }
guard "expired_hold_refresh" {
intent == CredentialUseIntent::HoldAuthority && self.credential_present
}
update {}
to Expired
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionExpiredBeginRefresh {
per_phase [Expired]
on input ResolveCredentialUseAdmission { intent }
guard "expired_begin_refresh" {
intent == CredentialUseIntent::BeginRefresh && self.credential_present
}
update {}
to Expired
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionExpiredNoCredential {
per_phase [Expired]
on input ResolveCredentialUseAdmission { intent }
guard "expired_no_credential" { self.credential_present == false }
update {}
to Expired
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::LeaseAbsent }
}
transition ResolveCredentialUseAdmissionRefreshingUseRefresh {
per_phase [Refreshing]
on input ResolveCredentialUseAdmission { intent }
guard "refreshing_use_refresh" {
intent == CredentialUseIntent::UseCredential && self.credential_present
}
update {}
to Refreshing
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveCredentialUseAdmissionRefreshingHoldAuthorized {
per_phase [Refreshing]
on input ResolveCredentialUseAdmission { intent }
guard "refreshing_hold_authorized" {
intent == CredentialUseIntent::HoldAuthority && self.credential_present
}
update {}
to Refreshing
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::Authorized }
}
transition ResolveCredentialUseAdmissionRefreshingBeginAlreadyRefreshing {
per_phase [Refreshing]
on input ResolveCredentialUseAdmission { intent }
guard "refreshing_begin_already" {
intent == CredentialUseIntent::BeginRefresh
}
update {}
to Refreshing
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::AlreadyRefreshing }
}
transition ResolveCredentialUseAdmissionRefreshingNoCredentialUseOrHold {
per_phase [Refreshing]
on input ResolveCredentialUseAdmission { intent }
guard "refreshing_no_credential" {
self.credential_present == false
&& (
intent == CredentialUseIntent::UseCredential
|| intent == CredentialUseIntent::HoldAuthority
)
}
update {}
to Refreshing
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::LeaseAbsent }
}
transition ResolveCredentialUseAdmissionReauthRequired {
per_phase [ReauthRequired]
on input ResolveCredentialUseAdmission { intent }
update {}
to ReauthRequired
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::ReauthRequired }
}
transition ResolveCredentialUseAdmissionReleased {
per_phase [Released]
on input ResolveCredentialUseAdmission { intent }
update {}
to Released
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::LeaseAbsent }
}
transition ResolveOAuthLoginCredentialDispositionUseCached {
per_phase [Valid]
on input ResolveOAuthLoginCredentialDisposition { credential_present, force_refresh, refresh_allowed }
guard "use_cached" {
self.credential_present
&& credential_present
&& force_refresh == false
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::Authorized }
}
transition ResolveOAuthLoginCredentialDispositionRefreshValid {
per_phase [Valid]
on input ResolveOAuthLoginCredentialDisposition { credential_present, force_refresh, refresh_allowed }
guard "needs_refresh_allowed" {
!(self.credential_present && credential_present && force_refresh == false)
&& refresh_allowed
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveOAuthLoginCredentialDispositionRefreshDisallowedValid {
per_phase [Valid]
on input ResolveOAuthLoginCredentialDisposition { credential_present, force_refresh, refresh_allowed }
guard "needs_refresh_disallowed" {
!(self.credential_present && credential_present && force_refresh == false)
&& refresh_allowed == false
}
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshDisallowed }
}
transition ResolveOAuthLoginCredentialDispositionRefreshNonValid {
per_phase [Expiring, Expired, Refreshing, ReauthRequired, Released]
on input ResolveOAuthLoginCredentialDisposition { credential_present, force_refresh, refresh_allowed }
guard "refresh_allowed" { refresh_allowed }
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshRequired }
}
transition ResolveOAuthLoginCredentialDispositionRefreshDisallowedNonValid {
per_phase [Expiring, Expired, Refreshing, ReauthRequired, Released]
on input ResolveOAuthLoginCredentialDisposition { credential_present, force_refresh, refresh_allowed }
guard "refresh_disallowed" { refresh_allowed == false }
update {}
to Valid
emit CredentialUseAdmissionResolved { disposition: CredentialUseDisposition::RefreshDisallowed }
}
}
}
};
}
crate::auth_catalog_machine_dsl!("self", "catalog::dsl::auth_machine");