Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit 17e968f

Browse files
committed
Record the user agent and IP in the device code grant
1 parent d39a1d2 commit 17e968f

14 files changed

+129
-20
lines changed

crates/data-model/src/oauth2/device_code_grant.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::net::IpAddr;
16+
1517
use chrono::{DateTime, Utc};
1618
use oauth2_types::scope::Scope;
1719
use serde::Serialize;
@@ -193,6 +195,12 @@ pub struct DeviceCodeGrant {
193195

194196
/// The time at which this device code grant will expire.
195197
pub expires_at: DateTime<Utc>,
198+
199+
/// The IP address of the client which requested this device code grant.
200+
pub ip_address: Option<IpAddr>,
201+
202+
/// The user agent used to request this device code grant.
203+
pub user_agent: Option<String>,
196204
}
197205

198206
impl std::ops::Deref for DeviceCodeGrant {

crates/handlers/src/activity_tracker/bound.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ impl Bound {
3333
Self { tracker, ip }
3434
}
3535

36+
/// Get the IP address bound to this activity tracker.
37+
#[must_use]
38+
pub fn ip(&self) -> Option<IpAddr> {
39+
self.ip
40+
}
41+
3642
/// Record activity in an OAuth 2.0 session.
3743
pub async fn record_oauth2_session(&self, clock: &dyn Clock, session: &Session) {
3844
self.tracker

crates/handlers/src/oauth2/device/authorize.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
use axum::{extract::State, response::IntoResponse, Json, TypedHeader};
1616
use chrono::Duration;
17-
use headers::{CacheControl, Pragma};
17+
use headers::{CacheControl, Pragma, UserAgent};
1818
use hyper::StatusCode;
1919
use mas_axum_utils::{
2020
client_authorization::{ClientAuthorization, CredentialsVerificationError},
@@ -32,7 +32,7 @@ use oauth2_types::{
3232
use rand::distributions::{Alphanumeric, DistString};
3333
use thiserror::Error;
3434

35-
use crate::impl_from_error_for_route;
35+
use crate::{impl_from_error_for_route, BoundActivityTracker};
3636

3737
#[derive(Debug, Error)]
3838
pub(crate) enum RouteError {
@@ -84,6 +84,8 @@ pub(crate) async fn post(
8484
mut rng: BoxRng,
8585
clock: BoxClock,
8686
mut repo: BoxRepository,
87+
user_agent: Option<TypedHeader<UserAgent>>,
88+
activity_tracker: BoundActivityTracker,
8789
State(url_builder): State<UrlBuilder>,
8890
State(http_client_factory): State<HttpClientFactory>,
8991
State(encrypter): State<Encrypter>,
@@ -123,6 +125,9 @@ pub(crate) async fn post(
123125

124126
let expires_in = Duration::minutes(20);
125127

128+
let user_agent = user_agent.map(|ua| ua.0.to_string());
129+
let ip_address = activity_tracker.ip();
130+
126131
let device_code = Alphanumeric.sample_string(&mut rng, 32);
127132
let user_code = Alphanumeric.sample_string(&mut rng, 6).to_uppercase();
128133

@@ -137,6 +142,8 @@ pub(crate) async fn post(
137142
device_code,
138143
user_code,
139144
expires_in,
145+
user_agent,
146+
ip_address,
140147
},
141148
)
142149
.await?;
Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/storage-pg/migrations/20231207090532_oauth_device_code_grant.sql

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,11 @@ CREATE TABLE "oauth2_device_code_grant" (
7272
-- The browser session ID that the user used to authenticate
7373
-- This means "fulfilled_at" or "rejected_at" has also been set
7474
"user_session_id" UUID
75-
REFERENCES "user_sessions" ("user_session_id")
75+
REFERENCES "user_sessions" ("user_session_id"),
76+
77+
-- The IP address of the user when they authenticated
78+
"ip_address" INET,
79+
80+
-- The user agent of the user when they authenticated
81+
"user_agent" TEXT
7682
);

crates/storage-pg/src/oauth2/device_code_grant.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::net::IpAddr;
16+
1517
use async_trait::async_trait;
1618
use chrono::{DateTime, Utc};
1719
use mas_data_model::{BrowserSession, DeviceCodeGrant, DeviceCodeGrantState, Session};
@@ -54,6 +56,8 @@ struct OAuth2DeviceGrantLookup {
5456
exchanged_at: Option<DateTime<Utc>>,
5557
user_session_id: Option<Uuid>,
5658
oauth2_session_id: Option<Uuid>,
59+
ip_address: Option<IpAddr>,
60+
user_agent: Option<String>,
5761
}
5862

5963
impl TryFrom<OAuth2DeviceGrantLookup> for DeviceCodeGrant {
@@ -73,6 +77,8 @@ impl TryFrom<OAuth2DeviceGrantLookup> for DeviceCodeGrant {
7377
exchanged_at,
7478
user_session_id,
7579
oauth2_session_id,
80+
ip_address,
81+
user_agent,
7682
}: OAuth2DeviceGrantLookup,
7783
) -> Result<Self, Self::Error> {
7884
let id = Ulid::from(oauth2_device_code_grant_id);
@@ -133,6 +139,8 @@ impl TryFrom<OAuth2DeviceGrantLookup> for DeviceCodeGrant {
133139
device_code,
134140
created_at,
135141
expires_at,
142+
ip_address,
143+
user_agent,
136144
})
137145
}
138146
}
@@ -176,9 +184,11 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
176184
, user_code
177185
, created_at
178186
, expires_at
187+
, ip_address
188+
, user_agent
179189
)
180190
VALUES
181-
($1, $2, $3, $4, $5, $6, $7)
191+
($1, $2, $3, $4, $5, $6, $7, $8, $9)
182192
"#,
183193
Uuid::from(id),
184194
Uuid::from(client_id),
@@ -187,6 +197,8 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
187197
&params.user_code,
188198
created_at,
189199
expires_at,
200+
params.ip_address as Option<IpAddr>,
201+
params.user_agent.as_deref(),
190202
)
191203
.traced()
192204
.execute(&mut *self.conn)
@@ -201,6 +213,8 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
201213
device_code: params.device_code,
202214
created_at,
203215
expires_at,
216+
ip_address: params.ip_address,
217+
user_agent: params.user_agent,
204218
})
205219
}
206220

@@ -229,6 +243,8 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
229243
, exchanged_at
230244
, user_session_id
231245
, oauth2_session_id
246+
, ip_address as "ip_address: IpAddr"
247+
, user_agent
232248
FROM
233249
oauth2_device_code_grant
234250
@@ -273,6 +289,8 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
273289
, exchanged_at
274290
, user_session_id
275291
, oauth2_session_id
292+
, ip_address as "ip_address: IpAddr"
293+
, user_agent
276294
FROM
277295
oauth2_device_code_grant
278296
@@ -317,6 +335,8 @@ impl<'c> OAuth2DeviceCodeGrantRepository for PgOAuth2DeviceCodeGrantRepository<'
317335
, exchanged_at
318336
, user_session_id
319337
, oauth2_session_id
338+
, ip_address as "ip_address: IpAddr"
339+
, user_agent
320340
FROM
321341
oauth2_device_code_grant
322342

crates/storage-pg/src/oauth2/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ mod tests {
758758
device_code: device_code.to_owned(),
759759
user_code: user_code.to_owned(),
760760
expires_in: Duration::minutes(5),
761+
ip_address: None,
762+
user_agent: None,
761763
},
762764
)
763765
.await
@@ -861,6 +863,8 @@ mod tests {
861863
device_code: "second_devicecode".to_owned(),
862864
user_code: "second_usercode".to_owned(),
863865
expires_in: Duration::minutes(5),
866+
ip_address: None,
867+
user_agent: None,
864868
},
865869
)
866870
.await

0 commit comments

Comments
 (0)