pub struct OAuthDeviceCode {Show 13 fields
pub device_code_digest: Digest,
pub user_code: String,
pub client_id: Uuid,
pub user_id: Option<Uuid>,
pub scopes: Vec<String>,
pub status: DeviceCodeStatus,
pub jti: Uuid,
pub interval_seconds: i32,
pub last_polled_at: Option<DateTime<Utc>>,
pub expires_at: DateTime<Utc>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub metadata: Value,
}Expand description
INTERNAL/DATABASE-ONLY MODEL - DO NOT EXPOSE TO CLIENTS
Contains a Digest field with raw bytes and custom serialization; never serialize this
directly into external API payloads. Use DTOs that strip or convert Digest fields instead.
Fields§
§device_code_digest: Digest§user_code: String§client_id: Uuid§user_id: Option<Uuid>None until the user approves the grant on the verification page.
scopes: Vec<String>§status: DeviceCodeStatus§jti: Uuid§interval_seconds: i32§last_polled_at: Option<DateTime<Utc>>§expires_at: DateTime<Utc>§created_at: DateTime<Utc>§updated_at: DateTime<Utc>§metadata: ValueImplementations§
Source§impl OAuthDeviceCode
impl OAuthDeviceCode
Sourcepub const EXPIRED_RETENTION: Duration
pub const EXPIRED_RETENTION: Duration
How long an expired device code is kept before it is prunable.
Deleting on the stroke of expiry would turn a device still polling into invalid_grant
instead of the expired_token RFC 8628 §3.5 prescribes; the grace period keeps that answer
truthful.
Sourcepub async fn insert(
conn: &mut PgConnection,
params: NewDeviceCodeParams<'_>,
) -> ModelResult<()>
pub async fn insert( conn: &mut PgConnection, params: NewDeviceCodeParams<'_>, ) -> ModelResult<()>
Insert a new pending device authorization grant.
Sourcepub async fn delete_expired(conn: &mut PgConnection) -> ModelResult<u64>
pub async fn delete_expired(conn: &mut PgConnection) -> ModelResult<u64>
Deletes device codes that expired more than [EXPIRED_RETENTION] ago, whatever their
status.
Only the approved-and-redeemed path deletes its own row, so without this every abandoned
login leaves a permanent row whose user_code is unusable — excluded by
expires_at > now() — yet unreclaimable, since the partial unique index still holds it
while status = 'pending'. Called opportunistically from the device-authorization
endpoint: the table only grows when that endpoint is used, so that is also where it can
shrink, with no CronJob to forget to deploy.
Sourcepub async fn find_pending_by_user_code(
conn: &mut PgConnection,
user_code: &str,
) -> ModelResult<OAuthDeviceCode>
pub async fn find_pending_by_user_code( conn: &mut PgConnection, user_code: &str, ) -> ModelResult<OAuthDeviceCode>
Find the still-valid, pending grant for a given user_code.
Used by the verification page to render the pending consent request. The partial unique index guarantees at most one pending row per code.
Sourcepub async fn approve(
conn: &mut PgConnection,
user_code: &str,
user_id: Uuid,
) -> ModelResult<OAuthDeviceCode>
pub async fn approve( conn: &mut PgConnection, user_code: &str, user_id: Uuid, ) -> ModelResult<OAuthDeviceCode>
Approve a pending grant, attaching the approving user in a single statement.
Only affects a row that is still pending and unexpired.
Sourcepub async fn deny(
conn: &mut PgConnection,
user_code: &str,
) -> ModelResult<OAuthDeviceCode>
pub async fn deny( conn: &mut PgConnection, user_code: &str, ) -> ModelResult<OAuthDeviceCode>
Deny a pending grant.
Only affects a row that is still pending and unexpired.
Sourcepub async fn record_poll(
conn: &mut PgConnection,
device_code_digest: &Digest,
) -> ModelResult<DeviceCodePoll>
pub async fn record_poll( conn: &mut PgConnection, device_code_digest: &Digest, ) -> ModelResult<DeviceCodePoll>
Record a poll from the token endpoint and return the state needed to decide the response.
Atomically reads the previous last_polled_at and advances it to now.
The returned previous_polled_at lets the caller detect a client
polling faster than interval_seconds (=> slow_down). Returns a
record-not-found error if the digest is unknown.
Sourcepub async fn consume_approved_in_transaction(
tx: &mut Transaction<'_, Postgres>,
device_code_digest: &Digest,
) -> ModelResult<OAuthDeviceCode>
pub async fn consume_approved_in_transaction( tx: &mut Transaction<'_, Postgres>, device_code_digest: &Digest, ) -> ModelResult<OAuthDeviceCode>
Single-use redemption of an approved device code within an existing transaction.
Deletes the approved, unexpired row and returns it, so a second
redemption of the same code finds nothing (single-use). The returned row
always has user_id = Some(..) (enforced by the approve statement and
the DB check constraint).
Trait Implementations§
Source§impl Debug for OAuthDeviceCode
impl Debug for OAuthDeviceCode
Source§impl<'de> Deserialize<'de> for OAuthDeviceCode
impl<'de> Deserialize<'de> for OAuthDeviceCode
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<'a, R: Row> FromRow<'a, R> for OAuthDeviceCodewhere
&'a str: ColumnIndex<R>,
Digest: Decode<'a, R::Database> + Type<R::Database>,
String: Decode<'a, R::Database> + Type<R::Database>,
Uuid: Decode<'a, R::Database> + Type<R::Database>,
Option<Uuid>: Decode<'a, R::Database> + Type<R::Database>,
Vec<String>: Decode<'a, R::Database> + Type<R::Database>,
DeviceCodeStatus: Decode<'a, R::Database> + Type<R::Database>,
i32: Decode<'a, R::Database> + Type<R::Database>,
Option<DateTime<Utc>>: Decode<'a, R::Database> + Type<R::Database>,
DateTime<Utc>: Decode<'a, R::Database> + Type<R::Database>,
Value: Decode<'a, R::Database> + Type<R::Database>,
impl<'a, R: Row> FromRow<'a, R> for OAuthDeviceCodewhere
&'a str: ColumnIndex<R>,
Digest: Decode<'a, R::Database> + Type<R::Database>,
String: Decode<'a, R::Database> + Type<R::Database>,
Uuid: Decode<'a, R::Database> + Type<R::Database>,
Option<Uuid>: Decode<'a, R::Database> + Type<R::Database>,
Vec<String>: Decode<'a, R::Database> + Type<R::Database>,
DeviceCodeStatus: Decode<'a, R::Database> + Type<R::Database>,
i32: Decode<'a, R::Database> + Type<R::Database>,
Option<DateTime<Utc>>: Decode<'a, R::Database> + Type<R::Database>,
DateTime<Utc>: Decode<'a, R::Database> + Type<R::Database>,
Value: Decode<'a, R::Database> + Type<R::Database>,
Auto Trait Implementations§
impl Freeze for OAuthDeviceCode
impl RefUnwindSafe for OAuthDeviceCode
impl Send for OAuthDeviceCode
impl Sync for OAuthDeviceCode
impl Unpin for OAuthDeviceCode
impl UnsafeUnpin for OAuthDeviceCode
impl UnwindSafe for OAuthDeviceCode
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].