Skip to content

Commit 51f5650

Browse files
authored
fix(auth): bind refresh token flow to issuing client (#14)
1 parent a4fb1c8 commit 51f5650

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

src/action/user/admin_initiate_auth.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ pub async fn handler(storage: &Storage, body: Value) -> Result<Value> {
149149
.await
150150
.ok_or(AppError::InvalidRefreshToken)?;
151151

152+
if stored_token.client_id != req.client_id {
153+
return Err(AppError::InvalidRefreshToken);
154+
}
155+
152156
if stored_token.expires_at < Utc::now() {
153157
return Err(AppError::InvalidRefreshToken);
154158
}
@@ -382,4 +386,78 @@ mod tests {
382386
assert!(result.is_err());
383387
assert!(matches!(result.unwrap_err(), AppError::UserPoolNotFound));
384388
}
389+
390+
#[tokio::test]
391+
async fn test_admin_initiate_auth_refresh_token_client_mismatch() {
392+
let storage = Storage::new();
393+
let (pool_id, client_id1) = setup_pool_and_client(&storage).await;
394+
395+
let client2 = create_user_pool_client::handler(
396+
&storage,
397+
json!({
398+
"UserPoolId": pool_id,
399+
"ClientName": "test-client-2"
400+
}),
401+
)
402+
.await
403+
.unwrap();
404+
let client_id2 = client2["UserPoolClient"]["ClientId"].as_str().unwrap();
405+
406+
admin_create_user::handler(
407+
&storage,
408+
json!({
409+
"UserPoolId": pool_id,
410+
"Username": "testuser"
411+
}),
412+
)
413+
.await
414+
.unwrap();
415+
admin_set_user_password::handler(
416+
&storage,
417+
json!({
418+
"UserPoolId": pool_id,
419+
"Username": "testuser",
420+
"Password": "Password123!",
421+
"Permanent": true
422+
}),
423+
)
424+
.await
425+
.unwrap();
426+
427+
let auth = handler(
428+
&storage,
429+
json!({
430+
"UserPoolId": pool_id,
431+
"ClientId": client_id1,
432+
"AuthFlow": "ADMIN_USER_PASSWORD_AUTH",
433+
"AuthParameters": {
434+
"USERNAME": "testuser",
435+
"PASSWORD": "Password123!"
436+
}
437+
}),
438+
)
439+
.await
440+
.unwrap();
441+
let refresh_token = auth["AuthenticationResult"]["RefreshToken"]
442+
.as_str()
443+
.unwrap();
444+
445+
let refresh_result = handler(
446+
&storage,
447+
json!({
448+
"UserPoolId": pool_id,
449+
"ClientId": client_id2,
450+
"AuthFlow": "REFRESH_TOKEN_AUTH",
451+
"AuthParameters": {
452+
"REFRESH_TOKEN": refresh_token
453+
}
454+
}),
455+
)
456+
.await;
457+
458+
assert!(matches!(
459+
refresh_result.unwrap_err(),
460+
AppError::InvalidRefreshToken
461+
));
462+
}
385463
}

src/action/user/initiate_auth.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ pub async fn handler(storage: &Storage, body: Value) -> Result<Value> {
130130
.await
131131
.ok_or(AppError::InvalidRefreshToken)?;
132132

133+
if stored_token.client_id != req.client_id {
134+
return Err(AppError::InvalidRefreshToken);
135+
}
136+
133137
if stored_token.expires_at < Utc::now() {
134138
return Err(AppError::InvalidRefreshToken);
135139
}
@@ -323,4 +327,57 @@ mod tests {
323327

324328
assert!(result.is_err());
325329
}
330+
331+
#[tokio::test]
332+
async fn test_initiate_auth_refresh_token_client_mismatch() {
333+
let storage = Storage::new();
334+
let (pool_id, client_id1) = setup_pool_and_client(&storage).await;
335+
336+
let client2 = create_user_pool_client::handler(
337+
&storage,
338+
json!({
339+
"UserPoolId": pool_id,
340+
"ClientName": "test-client-2"
341+
}),
342+
)
343+
.await
344+
.unwrap();
345+
let client_id2 = client2["UserPoolClient"]["ClientId"].as_str().unwrap();
346+
347+
create_confirmed_user(&storage, &client_id1, "testuser", "Password123!").await;
348+
349+
let auth = handler(
350+
&storage,
351+
json!({
352+
"ClientId": client_id1,
353+
"AuthFlow": "USER_PASSWORD_AUTH",
354+
"AuthParameters": {
355+
"USERNAME": "testuser",
356+
"PASSWORD": "Password123!"
357+
}
358+
}),
359+
)
360+
.await
361+
.unwrap();
362+
let refresh_token = auth["AuthenticationResult"]["RefreshToken"]
363+
.as_str()
364+
.unwrap();
365+
366+
let refresh_result = handler(
367+
&storage,
368+
json!({
369+
"ClientId": client_id2,
370+
"AuthFlow": "REFRESH_TOKEN_AUTH",
371+
"AuthParameters": {
372+
"REFRESH_TOKEN": refresh_token
373+
}
374+
}),
375+
)
376+
.await;
377+
378+
assert!(matches!(
379+
refresh_result.unwrap_err(),
380+
AppError::InvalidRefreshToken
381+
));
382+
}
326383
}

0 commit comments

Comments
 (0)