+77
src/pds.js
+77
src/pds.js
···
3728
3728
}
3729
3729
3730
3730
/**
3731
+
* Validate OAuth authorization request parameters.
3732
+
* Shared between PAR and direct authorization flows.
3733
+
* @param {Object} params - The authorization parameters
3734
+
* @param {string} params.clientId - The client_id
3735
+
* @param {string} params.redirectUri - The redirect_uri
3736
+
* @param {string} params.responseType - The response_type
3737
+
* @param {string} [params.responseMode] - The response_mode
3738
+
* @param {string} [params.scope] - The scope
3739
+
* @param {string} [params.state] - The state
3740
+
* @param {string} params.codeChallenge - The code_challenge
3741
+
* @param {string} params.codeChallengeMethod - The code_challenge_method
3742
+
* @param {string} [params.loginHint] - The login_hint
3743
+
* @returns {Promise<{error: Response} | {clientMetadata: ClientMetadata}>}
3744
+
*/
3745
+
async validateAuthorizationParameters({
3746
+
clientId,
3747
+
redirectUri,
3748
+
responseType,
3749
+
codeChallenge,
3750
+
codeChallengeMethod,
3751
+
}) {
3752
+
if (!clientId) {
3753
+
return { error: errorResponse('invalid_request', 'client_id required', 400) };
3754
+
}
3755
+
if (!redirectUri) {
3756
+
return { error: errorResponse('invalid_request', 'redirect_uri required', 400) };
3757
+
}
3758
+
if (responseType !== 'code') {
3759
+
return {
3760
+
error: errorResponse(
3761
+
'unsupported_response_type',
3762
+
'response_type must be code',
3763
+
400,
3764
+
),
3765
+
};
3766
+
}
3767
+
if (!codeChallenge || codeChallengeMethod !== 'S256') {
3768
+
return { error: errorResponse('invalid_request', 'PKCE with S256 required', 400) };
3769
+
}
3770
+
3771
+
let clientMetadata;
3772
+
try {
3773
+
clientMetadata = await getClientMetadata(clientId);
3774
+
} catch (err) {
3775
+
return { error: errorResponse('invalid_client', err.message, 400) };
3776
+
}
3777
+
3778
+
// Validate redirect_uri against registered URIs
3779
+
const isLoopback =
3780
+
clientId.startsWith('http://localhost') ||
3781
+
clientId.startsWith('http://127.0.0.1');
3782
+
const redirectUriValid = clientMetadata.redirect_uris.some((uri) => {
3783
+
if (isLoopback) {
3784
+
try {
3785
+
const registered = new URL(uri);
3786
+
const requested = new URL(redirectUri);
3787
+
return registered.origin === requested.origin;
3788
+
} catch {
3789
+
return false;
3790
+
}
3791
+
}
3792
+
return uri === redirectUri;
3793
+
});
3794
+
if (!redirectUriValid) {
3795
+
return {
3796
+
error: errorResponse(
3797
+
'invalid_request',
3798
+
'redirect_uri not registered for this client',
3799
+
400,
3800
+
),
3801
+
};
3802
+
}
3803
+
3804
+
return { clientMetadata };
3805
+
}
3806
+
3807
+
/**
3731
3808
* Handle Pushed Authorization Request (PAR) endpoint.
3732
3809
* Validates DPoP proof, client metadata, PKCE parameters, and stores the authorization request.
3733
3810
* @param {Request} request - The incoming request