+26
-265
src/oauth.rs
+26
-265
src/oauth.rs
···
359
.context("failed to compute expiration time")?
360
.timestamp();
361
362
-
// _ = sqlx::query!(
363
-
// r#"
364
-
// INSERT INTO oauth_par_requests (
365
-
// request_uri, client_id, response_type, code_challenge, code_challenge_method,
366
-
// state, login_hint, scope, redirect_uri, response_mode, display,
367
-
// created_at, expires_at
368
-
// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
369
-
// "#,
370
-
// request_uri,
371
-
// client_id,
372
-
// response_type,
373
-
// code_challenge,
374
-
// code_challenge_method,
375
-
// state,
376
-
// login_hint,
377
-
// scope,
378
-
// redirect_uri,
379
-
// response_mode,
380
-
// display,
381
-
// created_at,
382
-
// expires_at
383
-
// )
384
-
// .execute(&db)
385
-
// .await
386
-
// .context("failed to store PAR request")?;
387
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
388
let client_id = client_id.to_owned();
389
let request_uri_cloned = request_uri.to_owned();
···
449
let timestamp = chrono::Utc::now().timestamp();
450
451
// Retrieve the PAR request from the database
452
-
// let par_request = sqlx::query!(
453
-
// r#"
454
-
// SELECT * FROM oauth_par_requests
455
-
// WHERE request_uri = ? AND client_id = ? AND expires_at > ?
456
-
// "#,
457
-
// request_uri,
458
-
// client_id,
459
-
// timestamp
460
-
// )
461
-
// .fetch_optional(&db)
462
-
// .await
463
-
// .context("failed to query PAR request")?
464
-
// .context("PAR request not found or expired")?;
465
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
466
467
let request_uri_clone = request_uri.to_owned();
···
575
let timestamp = chrono::Utc::now().timestamp();
576
577
// Retrieve the PAR request
578
-
// let par_request = sqlx::query!(
579
-
// r#"
580
-
// SELECT * FROM oauth_par_requests
581
-
// WHERE request_uri = ? AND client_id = ? AND expires_at > ?
582
-
// "#,
583
-
// request_uri,
584
-
// client_id,
585
-
// timestamp
586
-
// )
587
-
// .fetch_optional(&db)
588
-
// .await
589
-
// .context("failed to query PAR request")?
590
-
// .context("PAR request not found or expired")?;
591
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
592
-
// diesel::table! {
593
-
// pds.oauth_par_requests (request_uri) {
594
-
// request_uri -> Varchar,
595
-
// client_id -> Varchar,
596
-
// response_type -> Varchar,
597
-
// code_challenge -> Varchar,
598
-
// code_challenge_method -> Varchar,
599
-
// state -> Nullable<Varchar>,
600
-
// login_hint -> Nullable<Varchar>,
601
-
// scope -> Nullable<Varchar>,
602
-
// redirect_uri -> Nullable<Varchar>,
603
-
// response_mode -> Nullable<Varchar>,
604
-
// display -> Nullable<Varchar>,
605
-
// created_at -> Int8,
606
-
// expires_at -> Int8,
607
-
// }
608
-
// }
609
#[derive(Queryable, Selectable)]
610
#[diesel(table_name = crate::schema::pds::oauth_par_requests)]
611
#[diesel(check_for_backend(sqlite::Sqlite))]
···
720
.context("failed to compute expiration time")?
721
.timestamp();
722
723
-
// _ = sqlx::query!(
724
-
// r#"
725
-
// INSERT INTO oauth_authorization_codes (
726
-
// code, client_id, subject, code_challenge, code_challenge_method,
727
-
// redirect_uri, scope, created_at, expires_at, used
728
-
// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
729
-
// "#,
730
-
// code,
731
-
// client_id,
732
-
// account.did,
733
-
// par_request.code_challenge,
734
-
// par_request.code_challenge_method,
735
-
// redirect_uri,
736
-
// par_request.scope,
737
-
// created_at,
738
-
// expires_at,
739
-
// false
740
-
// )
741
-
// .execute(&db)
742
-
// .await
743
-
// .context("failed to store authorization code")?;
744
use crate::schema::pds::oauth_authorization_codes::dsl as AuthCodeSchema;
745
let code_cloned = code.to_owned();
746
let client_id = client_id.to_owned();
···
971
}
972
973
// 11. Check for replay attacks via JTI tracking
974
-
// let jti_used =
975
-
// sqlx::query_scalar!(r#"SELECT COUNT(*) FROM oauth_used_jtis WHERE jti = ?"#, jti)
976
-
// .fetch_one(db)
977
-
// .await
978
-
// .context("failed to check JTI")?;
979
use crate::schema::pds::oauth_used_jtis::dsl as JtiSchema;
980
let jti_clone = jti.to_owned();
981
let jti_used = db
···
1002
}
1003
1004
// 12. Store the JTI to prevent replay attacks
1005
-
// _ = sqlx::query!(
1006
-
// r#"
1007
-
// INSERT INTO oauth_used_jtis (jti, issuer, created_at, expires_at)
1008
-
// VALUES (?, ?, ?, ?)
1009
-
// "#,
1010
-
// jti,
1011
-
// thumbprint, // Use thumbprint as issuer identifier
1012
-
// now,
1013
-
// exp
1014
-
// )
1015
-
// .execute(db)
1016
-
// .await
1017
-
// .context("failed to store JTI")?;
1018
let jti_cloned = jti.to_owned();
1019
let issuer = thumbprint.to_owned();
1020
let created_at = now;
···
1039
1040
// 13. Cleanup expired JTIs periodically (1% chance on each request)
1041
if thread_rng().gen_range(0_i32..100_i32) == 0_i32 {
1042
-
// _ = sqlx::query!(r#"DELETE FROM oauth_used_jtis WHERE expires_at < ?"#, now)
1043
-
// .execute(db)
1044
-
// .await
1045
-
// .context("failed to clean up expired JTIs")?;
1046
let now_clone = now.to_owned();
1047
_ = db
1048
.get()
···
1124
== "private_key_jwt";
1125
1126
// Verify DPoP proof
1127
-
let dpop_thumbprint = verify_dpop_proof(
1128
dpop_token,
1129
"POST",
1130
&format!("https://{}/oauth/token", config.host_name),
···
1170
// }
1171
} else {
1172
// Rule 2: For public clients, check if this DPoP key has been used before
1173
-
// let is_key_reused = sqlx::query_scalar!(
1174
-
// r#"SELECT COUNT(*) FROM oauth_refresh_tokens WHERE dpop_thumbprint = ? AND client_id = ?"#,
1175
-
// dpop_thumbprint,
1176
-
// client_id
1177
-
// )
1178
-
// .fetch_one(&db)
1179
-
// .await
1180
-
// .context("failed to check key usage history")? > 0;
1181
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1182
let is_key_reused = db
1183
.get()
1184
.await
1185
.expect("Failed to get database connection")
1186
.interact(move |conn| {
1187
RefreshTokenSchema::oauth_refresh_tokens
1188
-
.filter(RefreshTokenSchema::dpop_thumbprint.eq(dpop_thumbprint))
1189
-
.filter(RefreshTokenSchema::client_id.eq(client_id))
1190
.count()
1191
.get_result::<i64>(conn)
1192
.optional()
···
1219
let timestamp = chrono::Utc::now().timestamp();
1220
1221
// Retrieve and validate the authorization code
1222
-
// let auth_code = sqlx::query!(
1223
-
// r#"
1224
-
// SELECT * FROM oauth_authorization_codes
1225
-
// WHERE code = ? AND client_id = ? AND redirect_uri = ? AND expires_at > ? AND used = FALSE
1226
-
// "#,
1227
-
// code,
1228
-
// client_id,
1229
-
// redirect_uri,
1230
-
// timestamp
1231
-
// )
1232
-
// .fetch_optional(&db)
1233
-
// .await
1234
-
// .context("failed to query authorization code")?
1235
-
// .context("authorization code not found, expired, or already used")?;
1236
use crate::schema::pds::oauth_authorization_codes::dsl as AuthCodeSchema;
1237
-
// diesel::table! {
1238
-
// pds.oauth_authorization_codes (code) {
1239
-
// code -> Varchar,
1240
-
// client_id -> Varchar,
1241
-
// subject -> Varchar,
1242
-
// code_challenge -> Varchar,
1243
-
// code_challenge_method -> Varchar,
1244
-
// redirect_uri -> Varchar,
1245
-
// scope -> Nullable<Varchar>,
1246
-
// created_at -> Int8,
1247
-
// expires_at -> Int8,
1248
-
// used -> Bool,
1249
-
// }
1250
-
// }
1251
-
#[derive(Queryable, Selectable)]
1252
#[diesel(table_name = crate::schema::pds::oauth_authorization_codes)]
1253
#[diesel(check_for_backend(sqlite::Sqlite))]
1254
struct AuthCode {
···
1263
expires_at: i64,
1264
used: bool,
1265
}
1266
let auth_code = db
1267
.get()
1268
.await
1269
.expect("Failed to get database connection")
1270
.interact(move |conn| {
1271
AuthCodeSchema::oauth_authorization_codes
1272
-
.filter(AuthCodeSchema::code.eq(code))
1273
-
.filter(AuthCodeSchema::client_id.eq(client_id))
1274
-
.filter(AuthCodeSchema::redirect_uri.eq(redirect_uri))
1275
.filter(AuthCodeSchema::expires_at.gt(timestamp))
1276
.filter(AuthCodeSchema::used.eq(false))
1277
.first::<AuthCode>(conn)
···
1290
)?;
1291
1292
// Mark the code as used
1293
-
// _ = sqlx::query!(
1294
-
// r#"UPDATE oauth_authorization_codes SET used = TRUE WHERE code = ?"#,
1295
-
// code
1296
-
// )
1297
-
// .execute(&db)
1298
-
// .await
1299
-
// .context("failed to mark code as used")?;
1300
let code_cloned = code.to_owned();
1301
_ = db
1302
.get()
···
1334
"exp": access_token_expires_at,
1335
"iat": now,
1336
"cnf": {
1337
-
"jkt": dpop_thumbprint // Rule 1: Bind to DPoP key
1338
},
1339
"scope": auth_code.scope
1340
});
···
1350
"exp": refresh_token_expires_at,
1351
"iat": now,
1352
"cnf": {
1353
-
"jkt": dpop_thumbprint // Rule 1: Bind to DPoP key
1354
},
1355
"scope": auth_code.scope
1356
});
···
1359
.context("failed to sign refresh token")?;
1360
1361
// Store the refresh token with DPoP binding
1362
-
// _ = sqlx::query!(
1363
-
// r#"
1364
-
// INSERT INTO oauth_refresh_tokens (
1365
-
// token, client_id, subject, dpop_thumbprint, scope, created_at, expires_at, revoked
1366
-
// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1367
-
// "#,
1368
-
// refresh_token,
1369
-
// client_id,
1370
-
// auth_code.subject,
1371
-
// dpop_thumbprint,
1372
-
// auth_code.scope,
1373
-
// now,
1374
-
// refresh_token_expires_at,
1375
-
// false
1376
-
// )
1377
-
// .execute(&db)
1378
-
// .await
1379
-
// .context("failed to store refresh token")?;
1380
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1381
let refresh_token_cloned = refresh_token.to_owned();
1382
let client_id_cloned = client_id.to_owned();
1383
let subject = auth_code.subject.to_owned();
1384
-
let dpop_thumbprint_cloned = dpop_thumbprint.to_owned();
1385
let scope = auth_code.scope.to_owned();
1386
let created_at = now;
1387
let expires_at = refresh_token_expires_at;
···
1427
1428
// Rules 7 & 8: Verify refresh token and DPoP consistency
1429
// Retrieve the refresh token
1430
-
// let token_data = sqlx::query!(
1431
-
// r#"
1432
-
// SELECT * FROM oauth_refresh_tokens
1433
-
// WHERE token = ? AND client_id = ? AND expires_at > ? AND revoked = FALSE AND dpop_thumbprint = ?
1434
-
// "#,
1435
-
// refresh_token,
1436
-
// client_id,
1437
-
// timestamp,
1438
-
// dpop_thumbprint // Rule 8: Must use same DPoP key
1439
-
// )
1440
-
// .fetch_optional(&db)
1441
-
// .await
1442
-
// .context("failed to query refresh token")?
1443
-
// .context("refresh token not found, expired, revoked, or invalid for this DPoP key")?;
1444
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1445
-
// diesel::table! {
1446
-
// pds.oauth_refresh_tokens (token) {
1447
-
// token -> Varchar,
1448
-
// client_id -> Varchar,
1449
-
// subject -> Varchar,
1450
-
// dpop_thumbprint -> Varchar,
1451
-
// scope -> Nullable<Varchar>,
1452
-
// created_at -> Int8,
1453
-
// expires_at -> Int8,
1454
-
// revoked -> Bool,
1455
-
// }
1456
-
// }
1457
-
#[derive(Queryable, Selectable)]
1458
#[diesel(table_name = crate::schema::pds::oauth_refresh_tokens)]
1459
#[diesel(check_for_backend(sqlite::Sqlite))]
1460
struct TokenData {
···
1467
expires_at: i64,
1468
revoked: bool,
1469
}
1470
let token_data = db
1471
.get()
1472
.await
1473
.expect("Failed to get database connection")
1474
.interact(move |conn| {
1475
RefreshTokenSchema::oauth_refresh_tokens
1476
-
.filter(RefreshTokenSchema::token.eq(refresh_token))
1477
-
.filter(RefreshTokenSchema::client_id.eq(client_id))
1478
.filter(RefreshTokenSchema::expires_at.gt(timestamp))
1479
.filter(RefreshTokenSchema::revoked.eq(false))
1480
-
.filter(RefreshTokenSchema::dpop_thumbprint.eq(dpop_thumbprint))
1481
.first::<TokenData>(conn)
1482
.optional()
1483
})
···
1491
let client_still_advertises_key = true; // Implement actual check against client jwks
1492
if !client_still_advertises_key {
1493
// Revoke all tokens bound to this key
1494
-
// _ = sqlx::query!(
1495
-
// r#"UPDATE oauth_refresh_tokens SET revoked = TRUE
1496
-
// WHERE client_id = ? AND dpop_thumbprint = ?"#,
1497
-
// client_id,
1498
-
// dpop_thumbprint
1499
-
// )
1500
-
// .execute(&db)
1501
-
// .await
1502
-
// .context("failed to revoke tokens")?;
1503
let client_id_cloned = client_id.to_owned();
1504
-
let dpop_thumbprint_cloned = dpop_thumbprint.to_owned();
1505
_ = db
1506
.get()
1507
.await
···
1527
}
1528
1529
// Rotate the refresh token
1530
-
// _ = sqlx::query!(
1531
-
// r#"UPDATE oauth_refresh_tokens SET revoked = TRUE WHERE token = ?"#,
1532
-
// refresh_token
1533
-
// )
1534
-
// .execute(&db)
1535
-
// .await
1536
-
// .context("failed to revoke old refresh token")?;
1537
let refresh_token_cloned = refresh_token.to_owned();
1538
_ = db
1539
.get()
···
1566
"exp": access_token_expires_at,
1567
"iat": now,
1568
"cnf": {
1569
-
"jkt": dpop_thumbprint
1570
},
1571
"scope": token_data.scope
1572
});
···
1582
"exp": refresh_token_expires_at,
1583
"iat": now,
1584
"cnf": {
1585
-
"jkt": dpop_thumbprint
1586
},
1587
"scope": token_data.scope
1588
});
···
1591
.context("failed to sign refresh token")?;
1592
1593
// Store the new refresh token
1594
-
// _ = sqlx::query!(
1595
-
// r#"
1596
-
// INSERT INTO oauth_refresh_tokens (
1597
-
// token, client_id, subject, dpop_thumbprint, scope, created_at, expires_at, revoked
1598
-
// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1599
-
// "#,
1600
-
// new_refresh_token,
1601
-
// client_id,
1602
-
// token_data.subject,
1603
-
// dpop_thumbprint,
1604
-
// token_data.scope,
1605
-
// now,
1606
-
// refresh_token_expires_at,
1607
-
// false
1608
-
// )
1609
-
// .execute(&db)
1610
-
// .await
1611
-
// .context("failed to store refresh token")?;
1612
let new_refresh_token_cloned = new_refresh_token.to_owned();
1613
let client_id_cloned = client_id.to_owned();
1614
let subject = token_data.subject.to_owned();
1615
-
let dpop_thumbprint_cloned = dpop_thumbprint.to_owned();
1616
let scope = token_data.scope.to_owned();
1617
let created_at = now;
1618
let expires_at = refresh_token_expires_at;
···
1732
}
1733
1734
// Revoke the token
1735
-
// _ = sqlx::query!(
1736
-
// r#"UPDATE oauth_refresh_tokens SET revoked = TRUE WHERE token = ?"#,
1737
-
// token
1738
-
// )
1739
-
// .execute(&db)
1740
-
// .await
1741
-
// .context("failed to revoke token")?;
1742
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1743
let token_cloned = token.to_owned();
1744
_ = db
···
1807
1808
// For refresh tokens, check if it's been revoked
1809
if is_refresh_token {
1810
-
// let is_revoked = sqlx::query_scalar!(
1811
-
// r#"SELECT revoked FROM oauth_refresh_tokens WHERE token = ?"#,
1812
-
// token
1813
-
// )
1814
-
// .fetch_optional(&db)
1815
-
// .await
1816
-
// .context("failed to query token")?
1817
-
// .unwrap_or(true);
1818
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1819
let token_cloned = token.to_owned();
1820
let is_revoked = db
···
359
.context("failed to compute expiration time")?
360
.timestamp();
361
362
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
363
let client_id = client_id.to_owned();
364
let request_uri_cloned = request_uri.to_owned();
···
424
let timestamp = chrono::Utc::now().timestamp();
425
426
// Retrieve the PAR request from the database
427
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
428
429
let request_uri_clone = request_uri.to_owned();
···
537
let timestamp = chrono::Utc::now().timestamp();
538
539
// Retrieve the PAR request
540
use crate::schema::pds::oauth_par_requests::dsl as ParRequestSchema;
541
#[derive(Queryable, Selectable)]
542
#[diesel(table_name = crate::schema::pds::oauth_par_requests)]
543
#[diesel(check_for_backend(sqlite::Sqlite))]
···
652
.context("failed to compute expiration time")?
653
.timestamp();
654
655
use crate::schema::pds::oauth_authorization_codes::dsl as AuthCodeSchema;
656
let code_cloned = code.to_owned();
657
let client_id = client_id.to_owned();
···
882
}
883
884
// 11. Check for replay attacks via JTI tracking
885
use crate::schema::pds::oauth_used_jtis::dsl as JtiSchema;
886
let jti_clone = jti.to_owned();
887
let jti_used = db
···
908
}
909
910
// 12. Store the JTI to prevent replay attacks
911
let jti_cloned = jti.to_owned();
912
let issuer = thumbprint.to_owned();
913
let created_at = now;
···
932
933
// 13. Cleanup expired JTIs periodically (1% chance on each request)
934
if thread_rng().gen_range(0_i32..100_i32) == 0_i32 {
935
let now_clone = now.to_owned();
936
_ = db
937
.get()
···
1013
== "private_key_jwt";
1014
1015
// Verify DPoP proof
1016
+
let dpop_thumbprint_res = verify_dpop_proof(
1017
dpop_token,
1018
"POST",
1019
&format!("https://{}/oauth/token", config.host_name),
···
1059
// }
1060
} else {
1061
// Rule 2: For public clients, check if this DPoP key has been used before
1062
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1063
+
let dpop_thumbprint_clone = dpop_thumbprint_res.to_owned();
1064
+
let client_id_clone = client_id.to_owned();
1065
let is_key_reused = db
1066
.get()
1067
.await
1068
.expect("Failed to get database connection")
1069
.interact(move |conn| {
1070
RefreshTokenSchema::oauth_refresh_tokens
1071
+
.filter(RefreshTokenSchema::dpop_thumbprint.eq(dpop_thumbprint_clone))
1072
+
.filter(RefreshTokenSchema::client_id.eq(client_id_clone))
1073
.count()
1074
.get_result::<i64>(conn)
1075
.optional()
···
1102
let timestamp = chrono::Utc::now().timestamp();
1103
1104
// Retrieve and validate the authorization code
1105
use crate::schema::pds::oauth_authorization_codes::dsl as AuthCodeSchema;
1106
+
#[derive(Queryable, Selectable, Serialize)]
1107
#[diesel(table_name = crate::schema::pds::oauth_authorization_codes)]
1108
#[diesel(check_for_backend(sqlite::Sqlite))]
1109
struct AuthCode {
···
1118
expires_at: i64,
1119
used: bool,
1120
}
1121
+
let code_clone = code.to_owned();
1122
+
let client_id_clone = client_id.to_owned();
1123
+
let redirect_uri_clone = redirect_uri.to_owned();
1124
let auth_code = db
1125
.get()
1126
.await
1127
.expect("Failed to get database connection")
1128
.interact(move |conn| {
1129
AuthCodeSchema::oauth_authorization_codes
1130
+
.filter(AuthCodeSchema::code.eq(code_clone))
1131
+
.filter(AuthCodeSchema::client_id.eq(client_id_clone))
1132
+
.filter(AuthCodeSchema::redirect_uri.eq(redirect_uri_clone))
1133
.filter(AuthCodeSchema::expires_at.gt(timestamp))
1134
.filter(AuthCodeSchema::used.eq(false))
1135
.first::<AuthCode>(conn)
···
1148
)?;
1149
1150
// Mark the code as used
1151
let code_cloned = code.to_owned();
1152
_ = db
1153
.get()
···
1185
"exp": access_token_expires_at,
1186
"iat": now,
1187
"cnf": {
1188
+
"jkt": dpop_thumbprint_res // Rule 1: Bind to DPoP key
1189
},
1190
"scope": auth_code.scope
1191
});
···
1201
"exp": refresh_token_expires_at,
1202
"iat": now,
1203
"cnf": {
1204
+
"jkt": dpop_thumbprint_res // Rule 1: Bind to DPoP key
1205
},
1206
"scope": auth_code.scope
1207
});
···
1210
.context("failed to sign refresh token")?;
1211
1212
// Store the refresh token with DPoP binding
1213
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1214
let refresh_token_cloned = refresh_token.to_owned();
1215
let client_id_cloned = client_id.to_owned();
1216
let subject = auth_code.subject.to_owned();
1217
+
let dpop_thumbprint_cloned = dpop_thumbprint_res.to_owned();
1218
let scope = auth_code.scope.to_owned();
1219
let created_at = now;
1220
let expires_at = refresh_token_expires_at;
···
1260
1261
// Rules 7 & 8: Verify refresh token and DPoP consistency
1262
// Retrieve the refresh token
1263
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1264
+
#[derive(Queryable, Selectable, Serialize)]
1265
#[diesel(table_name = crate::schema::pds::oauth_refresh_tokens)]
1266
#[diesel(check_for_backend(sqlite::Sqlite))]
1267
struct TokenData {
···
1274
expires_at: i64,
1275
revoked: bool,
1276
}
1277
+
let dpop_thumbprint_clone = dpop_thumbprint_res.to_owned();
1278
+
let refresh_token_clone = refresh_token.to_owned();
1279
+
let client_id_clone = client_id.to_owned();
1280
let token_data = db
1281
.get()
1282
.await
1283
.expect("Failed to get database connection")
1284
.interact(move |conn| {
1285
RefreshTokenSchema::oauth_refresh_tokens
1286
+
.filter(RefreshTokenSchema::token.eq(refresh_token_clone))
1287
+
.filter(RefreshTokenSchema::client_id.eq(client_id_clone))
1288
.filter(RefreshTokenSchema::expires_at.gt(timestamp))
1289
.filter(RefreshTokenSchema::revoked.eq(false))
1290
+
.filter(RefreshTokenSchema::dpop_thumbprint.eq(dpop_thumbprint_clone))
1291
.first::<TokenData>(conn)
1292
.optional()
1293
})
···
1301
let client_still_advertises_key = true; // Implement actual check against client jwks
1302
if !client_still_advertises_key {
1303
// Revoke all tokens bound to this key
1304
let client_id_cloned = client_id.to_owned();
1305
+
let dpop_thumbprint_cloned = dpop_thumbprint_res.to_owned();
1306
_ = db
1307
.get()
1308
.await
···
1328
}
1329
1330
// Rotate the refresh token
1331
let refresh_token_cloned = refresh_token.to_owned();
1332
_ = db
1333
.get()
···
1360
"exp": access_token_expires_at,
1361
"iat": now,
1362
"cnf": {
1363
+
"jkt": dpop_thumbprint_res
1364
},
1365
"scope": token_data.scope
1366
});
···
1376
"exp": refresh_token_expires_at,
1377
"iat": now,
1378
"cnf": {
1379
+
"jkt": dpop_thumbprint_res
1380
},
1381
"scope": token_data.scope
1382
});
···
1385
.context("failed to sign refresh token")?;
1386
1387
// Store the new refresh token
1388
let new_refresh_token_cloned = new_refresh_token.to_owned();
1389
let client_id_cloned = client_id.to_owned();
1390
let subject = token_data.subject.to_owned();
1391
+
let dpop_thumbprint_cloned = dpop_thumbprint_res.to_owned();
1392
let scope = token_data.scope.to_owned();
1393
let created_at = now;
1394
let expires_at = refresh_token_expires_at;
···
1508
}
1509
1510
// Revoke the token
1511
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1512
let token_cloned = token.to_owned();
1513
_ = db
···
1576
1577
// For refresh tokens, check if it's been revoked
1578
if is_refresh_token {
1579
use crate::schema::pds::oauth_refresh_tokens::dsl as RefreshTokenSchema;
1580
let token_cloned = token.to_owned();
1581
let is_revoked = db