Skip to content

Commit

Permalink
fix: scope and par fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra committed Jul 30, 2024
1 parent 006d953 commit 71e72aa
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 58 deletions.
23 changes: 11 additions & 12 deletions packages/client/lib/AuthorizationCodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,16 @@ export const createAuthorizationRequestUrl = async ({
const { redirectUri, requestObjectOpts = { requestObjectMode: CreateRequestObjectMode.NONE } } = authorizationRequest;
const client_id = clientId ?? authorizationRequest.clientId;

let { scope, authorizationDetails } = authorizationRequest;
const parMode = endpointMetadata?.credentialIssuerMetadata?.require_pushed_authorization_requests
// Authorization server metadata takes precedence
const authorizationMetadata = endpointMetadata.authorizationServerMetadata ?? endpointMetadata.credentialIssuerMetadata

let { authorizationDetails } = authorizationRequest;
const parMode = authorizationMetadata?.require_pushed_authorization_requests
? PARMode.REQUIRE
: authorizationRequest.parMode ?? (client_id ? PARMode.AUTO : PARMode.NEVER);
: (authorizationRequest.parMode ?? (client_id ? PARMode.AUTO : PARMode.NEVER));
// Scope and authorization_details can be used in the same authorization request
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param
if (!scope && !authorizationDetails) {
if (!authorizationRequest.scope && !authorizationDetails) {
if (!credentialOffer) {
throw Error('Please provide a scope or authorization_details if no credential offer is present');
}
Expand Down Expand Up @@ -177,12 +180,8 @@ export const createAuthorizationRequestUrl = async ({
if (!endpointMetadata?.authorization_endpoint) {
throw Error('Server metadata does not contain authorization endpoint');
}
const parEndpoint = endpointMetadata.credentialIssuerMetadata?.pushed_authorization_request_endpoint;
const parEndpoint = authorizationMetadata?.pushed_authorization_request_endpoint;

// add 'openid' scope if not present
if (!scope?.includes('openid')) {
scope = ['openid', scope].filter((s) => !!s).join(' ');
}

let queryObj: Record<string, any> | PushedAuthorizationResponse = {
response_type: ResponseType.AUTH_CODE,
Expand All @@ -194,7 +193,7 @@ export const createAuthorizationRequestUrl = async ({
...(redirectUri && { redirect_uri: redirectUri }),
...(client_id && { client_id }),
...(credentialOffer?.issuerState && { issuer_state: credentialOffer.issuerState }),
scope,
scope: authorizationRequest.scope,
};

if (!parEndpoint && parMode === PARMode.REQUIRE) {
Expand All @@ -210,11 +209,11 @@ export const createAuthorizationRequestUrl = async ({
{ contentType: 'application/x-www-form-urlencoded', accept: 'application/json' },
);
if (parResponse.errorBody || !parResponse.successBody) {
console.log(JSON.stringify(parResponse.errorBody));
console.log('Falling back to regular request URI, since PAR failed');
if (parMode === PARMode.REQUIRE) {
throw Error(`PAR error: ${parResponse.origResponse.statusText}`);
}

debug('Falling back to regular request URI, since PAR failed', JSON.stringify(parResponse.errorBody));
} else {
debug(`PAR response: ${JSON.stringify(parResponse.successBody, null, 2)}`);
queryObj = { /*response_type: ResponseType.AUTH_CODE,*/ client_id, request_uri: parResponse.successBody.request_uri };
Expand Down
25 changes: 2 additions & 23 deletions packages/client/lib/__tests__/OpenID4VCIClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,6 @@ describe('OpenID4VCIClient should', () => {
}),
).rejects.toThrow(Error('Server metadata does not contain authorization endpoint'));
});
it("injects 'openid' as the first scope if not provided", async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

const url = await client.createAuthorizationRequestUrl({
pkce: {
codeChallengeMethod: CodeChallengeMethod.S256,
codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
},
authorizationRequest: {
scope: 'TestCredential',
redirectUri: 'http://localhost:8881/cb',
},
});

const urlSearchParams = new URLSearchParams(url.split('?')[1]);
const scope = urlSearchParams.get('scope')?.split(' ');

expect(scope?.[0]).toBe('openid');
});
it('throw an error if no scope and no authorization_details is provided', async () => {
nock(MOCK_URL).get(/.*/).reply(200, {});
nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, {});
Expand Down Expand Up @@ -149,7 +128,7 @@ describe('OpenID4VCIClient should', () => {
},
}),
).resolves.toEqual(
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client',
);
});
it('create an authorization request url with authorization_details object property', async () => {
Expand All @@ -176,7 +155,7 @@ describe('OpenID4VCIClient should', () => {
},
}),
).resolves.toEqual(
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client',
);
});

Expand Down
25 changes: 2 additions & 23 deletions packages/client/lib/__tests__/OpenID4VCIClientV1_0_13.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,6 @@ describe('OpenID4VCIClientV1_0_13 should', () => {
}),
).rejects.toThrow(Error('Server metadata does not contain authorization endpoint'));
});
it("injects 'openid' as the first scope if not provided", async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._state.endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

const url = await client.createAuthorizationRequestUrl({
pkce: {
codeChallengeMethod: CodeChallengeMethod.S256,
codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs',
},
authorizationRequest: {
scope: 'TestCredential',
redirectUri: 'http://localhost:8881/cb',
},
});

const urlSearchParams = new URLSearchParams(url.split('?')[1]);
const scope = urlSearchParams.get('scope')?.split(' ');

expect(scope?.[0]).toBe('openid');
});
it('throw an error if no scope and no authorization_details is provided', async () => {
nock(MOCK_URL).get(/.*/).reply(200, {});
nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(200, {});
Expand Down Expand Up @@ -141,7 +120,7 @@ describe('OpenID4VCIClientV1_0_13 should', () => {
},
}),
).resolves.toEqual(
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client',
);
});
it('create an authorization request url with authorization_details object property', async () => {
Expand All @@ -168,7 +147,7 @@ describe('OpenID4VCIClientV1_0_13 should', () => {
},
}),
).resolves.toEqual(
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client&scope=openid',
'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%5B%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&client_id=test-client',
);
});

Expand Down

0 comments on commit 71e72aa

Please sign in to comment.