Skip to content

Commit e1980ff

Browse files
committed
Accept oauth client id and secret via basic auth
1 parent 4c664d8 commit e1980ff

File tree

1 file changed

+59
-29
lines changed
  • v-api/src/endpoints/login/oauth

1 file changed

+59
-29
lines changed

v-api/src/endpoints/login/oauth/code.rs

+59-29
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
66
use chrono::{TimeDelta, Utc};
77
use dropshot::{
8-
http_response_temporary_redirect, Body, ClientErrorStatusCode, HttpError, HttpResponseOk,
9-
HttpResponseTemporaryRedirect, Path, Query, RequestContext, RequestInfo, TypedBody,
8+
http_response_temporary_redirect, Body, ClientErrorStatusCode, ExclusiveExtractor, HttpError,
9+
HttpResponseOk, HttpResponseTemporaryRedirect, Path, Query, RequestContext, RequestInfo,
10+
SharedExtractor, TypedBody,
1011
};
12+
use dropshot_authorization_header::basic::BasicAuth;
1113
use http::{
1214
header::{LOCATION, SET_COOKIE},
1315
HeaderValue, StatusCode,
@@ -18,7 +20,7 @@ use oauth2::{
1820
AuthorizationCode, CsrfToken, PkceCodeChallenge, PkceCodeVerifier, Scope, TokenResponse,
1921
};
2022
use schemars::JsonSchema;
21-
use secrecy::SecretString;
23+
use secrecy::{ExposeSecret, SecretString};
2224
use serde::{Deserialize, Serialize};
2325
use sha2::{Digest, Sha256};
2426
use std::{fmt::Debug, ops::Add};
@@ -121,7 +123,7 @@ where
121123
{
122124
let client = ctx
123125
.oauth
124-
.get_oauth_client(&ctx.builtin_registration_user(), &client_id)
126+
.get_oauth_client(&ctx.builtin_registration_user(), client_id)
125127
.await
126128
.map_err(|err| {
127129
tracing::error!(?err, "Failed to lookup OAuth client");
@@ -437,8 +439,8 @@ where
437439

438440
#[derive(Debug, Deserialize, JsonSchema)]
439441
pub struct OAuthAuthzCodeExchangeBody {
440-
pub client_id: TypedUuid<OAuthClientId>,
441-
pub client_secret: OpenApiSecretString,
442+
pub client_id: Option<TypedUuid<OAuthClientId>>,
443+
pub client_secret: Option<OpenApiSecretString>,
442444
pub redirect_uri: String,
443445
pub grant_type: String,
444446
pub code: String,
@@ -464,6 +466,34 @@ where
464466
let ctx = rqctx.v_ctx();
465467
let path = path.into_inner();
466468
let body = body.into_inner();
469+
470+
let (client_id, client_secret) =
471+
if let (Some(client_id), Some(client_secret)) = (body.client_id, body.client_secret) {
472+
Ok::<_, HttpError>((client_id, client_secret))
473+
} else {
474+
// Attempt to extract basic authorization credentials from the request if they were not
475+
// present in the request body
476+
let auth = <BasicAuth as SharedExtractor>::from_request(rqctx)
477+
.await
478+
.tap_err(|err| {
479+
tracing::warn!(?err, "Failed to extract basic authentication values");
480+
});
481+
let (client_id, client_secret) = match auth {
482+
Ok(auth) if auth.username().is_some() && auth.password().is_some() => Ok((
483+
auth.username().unwrap().to_string(),
484+
auth.password().unwrap().to_string(),
485+
)),
486+
_ => Err(internal_error(
487+
"Missing client id and client secret from authz code exchange",
488+
)),
489+
}?;
490+
491+
Ok((
492+
client_id.parse().map_err(to_internal_error)?,
493+
OpenApiSecretString(client_secret.into()),
494+
))
495+
}?;
496+
467497
let provider = ctx
468498
.get_oauth_provider(&path.provider)
469499
.await
@@ -475,8 +505,8 @@ where
475505
authorize_code_exchange(
476506
&ctx,
477507
&body.grant_type,
478-
&body.client_id,
479-
&body.client_secret.0,
508+
client_id,
509+
&client_secret.0,
480510
&body.redirect_uri,
481511
)
482512
.await?;
@@ -499,7 +529,7 @@ where
499529
// Verify that the login attempt is valid and matches the submitted client credentials
500530
verify_login_attempt(
501531
&attempt,
502-
&body.client_id,
532+
client_id,
503533
&body.redirect_uri,
504534
body.pkce_verifier.as_deref(),
505535
)?;
@@ -544,7 +574,7 @@ where
544574
async fn authorize_code_exchange<T>(
545575
ctx: &VContext<T>,
546576
grant_type: &str,
547-
client_id: &TypedUuid<OAuthClientId>,
577+
client_id: TypedUuid<OAuthClientId>,
548578
client_secret: &SecretString,
549579
redirect_uri: &str,
550580
) -> Result<(), OAuthError>
@@ -594,11 +624,11 @@ where
594624

595625
fn verify_login_attempt(
596626
attempt: &LoginAttempt,
597-
client_id: &TypedUuid<OAuthClientId>,
627+
client_id: TypedUuid<OAuthClientId>,
598628
redirect_uri: &str,
599629
pkce_verifier: Option<&str>,
600630
) -> Result<(), OAuthError> {
601-
if attempt.client_id != *client_id {
631+
if attempt.client_id != client_id {
602632
Err(OAuthError {
603633
error: OAuthErrorCode::InvalidGrant,
604634
error_description: Some("Invalid client id".to_string()),
@@ -1238,7 +1268,7 @@ mod tests {
12381268
authorize_code_exchange(
12391269
&ctx,
12401270
"authorization_code",
1241-
&wrong_client_id,
1271+
wrong_client_id,
12421272
&client_secret,
12431273
&redirect_uri,
12441274
)
@@ -1253,7 +1283,7 @@ mod tests {
12531283
authorize_code_exchange(
12541284
&ctx,
12551285
"authorization_code",
1256-
&client_id,
1286+
client_id,
12571287
&client_secret,
12581288
"wrong-callback-destination",
12591289
)
@@ -1268,7 +1298,7 @@ mod tests {
12681298
authorize_code_exchange(
12691299
&ctx,
12701300
"authorization_code",
1271-
&client_id,
1301+
client_id,
12721302
&client_secret,
12731303
&redirect_uri,
12741304
)
@@ -1299,7 +1329,7 @@ mod tests {
12991329
authorize_code_exchange(
13001330
&ctx,
13011331
"not_authorization_code",
1302-
&client_id,
1332+
client_id,
13031333
&client_secret,
13041334
&redirect_uri
13051335
)
@@ -1313,7 +1343,7 @@ mod tests {
13131343
authorize_code_exchange(
13141344
&ctx,
13151345
"authorization_code",
1316-
&client_id,
1346+
client_id,
13171347
&client_secret,
13181348
&redirect_uri
13191349
)
@@ -1351,7 +1381,7 @@ mod tests {
13511381
authorize_code_exchange(
13521382
&ctx,
13531383
"authorization_code",
1354-
&client_id,
1384+
client_id,
13551385
&"too-short".to_string().into(),
13561386
&redirect_uri
13571387
)
@@ -1365,7 +1395,7 @@ mod tests {
13651395
authorize_code_exchange(
13661396
&ctx,
13671397
"authorization_code",
1368-
&client_id,
1398+
client_id,
13691399
&invalid_secret.into(),
13701400
&redirect_uri
13711401
)
@@ -1379,7 +1409,7 @@ mod tests {
13791409
authorize_code_exchange(
13801410
&ctx,
13811411
"authorization_code",
1382-
&client_id,
1412+
client_id,
13831413
&client_secret,
13841414
&redirect_uri
13851415
)
@@ -1425,7 +1455,7 @@ mod tests {
14251455
},
14261456
verify_login_attempt(
14271457
&bad_client_id,
1428-
&attempt.client_id,
1458+
attempt.client_id,
14291459
&attempt.redirect_uri,
14301460
Some(verifier.secret().as_str()),
14311461
)
@@ -1446,7 +1476,7 @@ mod tests {
14461476
},
14471477
verify_login_attempt(
14481478
&bad_redirect_uri,
1449-
&attempt.client_id,
1479+
attempt.client_id,
14501480
&attempt.redirect_uri,
14511481
Some(verifier.secret().as_str()),
14521482
)
@@ -1467,7 +1497,7 @@ mod tests {
14671497
},
14681498
verify_login_attempt(
14691499
&unconfirmed_state,
1470-
&attempt.client_id,
1500+
attempt.client_id,
14711501
&attempt.redirect_uri,
14721502
Some(verifier.secret().as_str()),
14731503
)
@@ -1488,7 +1518,7 @@ mod tests {
14881518
},
14891519
verify_login_attempt(
14901520
&already_used_state,
1491-
&attempt.client_id,
1521+
attempt.client_id,
14921522
&attempt.redirect_uri,
14931523
Some(verifier.secret().as_str()),
14941524
)
@@ -1509,7 +1539,7 @@ mod tests {
15091539
},
15101540
verify_login_attempt(
15111541
&failed_state,
1512-
&attempt.client_id,
1542+
attempt.client_id,
15131543
&attempt.redirect_uri,
15141544
Some(verifier.secret().as_str()),
15151545
)
@@ -1530,7 +1560,7 @@ mod tests {
15301560
},
15311561
verify_login_attempt(
15321562
&expired,
1533-
&attempt.client_id,
1563+
attempt.client_id,
15341564
&attempt.redirect_uri,
15351565
Some(verifier.secret().as_str()),
15361566
)
@@ -1548,7 +1578,7 @@ mod tests {
15481578
},
15491579
verify_login_attempt(
15501580
&missing_pkce,
1551-
&attempt.client_id,
1581+
attempt.client_id,
15521582
&attempt.redirect_uri,
15531583
None,
15541584
)
@@ -1569,7 +1599,7 @@ mod tests {
15691599
},
15701600
verify_login_attempt(
15711601
&invalid_pkce,
1572-
&attempt.client_id,
1602+
attempt.client_id,
15731603
&attempt.redirect_uri,
15741604
Some(verifier.secret().as_str()),
15751605
)
@@ -1580,7 +1610,7 @@ mod tests {
15801610
(),
15811611
verify_login_attempt(
15821612
&attempt,
1583-
&attempt.client_id,
1613+
attempt.client_id,
15841614
&attempt.redirect_uri,
15851615
Some(verifier.secret().as_str()),
15861616
)

0 commit comments

Comments
 (0)