Skip to content

Commit

Permalink
fix: PAR objects where in the wrong locations and one had a wrong name
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Apr 7, 2023
1 parent 9de6164 commit 24f98e7
Show file tree
Hide file tree
Showing 9 changed files with 1,383 additions and 1,450 deletions.
24 changes: 12 additions & 12 deletions packages/client/lib/MetadataClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ export class MetadataClient {
let token_endpoint;
let credential_endpoint;
const response = await MetadataClient.retrieveOpenID4VCIServerMetadata(issuer);
let oid4vciMetadata = response?.successBody;
if (oid4vciMetadata) {
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${oid4vciMetadata}`);
credential_endpoint = oid4vciMetadata.credential_endpoint;
token_endpoint = oid4vciMetadata.token_endpoint;
if (!token_endpoint && oid4vciMetadata.authorization_server) {
let issuerMetadata = response?.successBody;
if (issuerMetadata) {
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${issuerMetadata}`);
credential_endpoint = issuerMetadata.credential_endpoint;
token_endpoint = issuerMetadata.token_endpoint;
if (!token_endpoint && issuerMetadata.authorization_server) {
debug(
`Issuer ${issuer} OID4VCI metadata has separate authorization_server ${oid4vciMetadata.authorization_server} that contains the token endpoint`
`Issuer ${issuer} OID4VCI metadata has separate authorization_server ${issuerMetadata.authorization_server} that contains the token endpoint`
);
// Crossword uses this to separate the AS metadata. We fail when not found, since we now have no way of getting the token endpoint
const response: OpenIDResponse<OAuth2ASMetadata> = await this.retrieveWellknown(
oid4vciMetadata.authorization_server,
issuerMetadata.authorization_server,
WellKnownEndpoints.OAUTH_AS,
{
errorOnNotFound: true,
Expand All @@ -80,9 +80,9 @@ export class MetadataClient {
}
if (asConfig) {
debug(`Issuer ${issuer} has oAuth2 Server metadata in well-known location`);
oid4vciMetadata = asConfig;
credential_endpoint = oid4vciMetadata.credential_endpoint;
token_endpoint = oid4vciMetadata.token_endpoint;
issuerMetadata = asConfig;
credential_endpoint = issuerMetadata.credential_endpoint;
token_endpoint = issuerMetadata.token_endpoint;
}
}
if (!token_endpoint) {
Expand All @@ -106,7 +106,7 @@ export class MetadataClient {
issuer,
token_endpoint,
credential_endpoint,
openid4vci_metadata: oid4vciMetadata,
issuerMetadata,
};
}

Expand Down
62 changes: 32 additions & 30 deletions packages/client/lib/OpenID4VCIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
CredentialSupported,
EndpointMetadata,
IssuerCredentialSubject,
OpenIDResponse,
ProofOfPossessionCallbacks,
PushedAuthorizationResponse,
ResponseType,
Expand Down Expand Up @@ -50,7 +51,7 @@ export class OpenID4VCIClient {
private _clientId?: string;
private _kid: string | undefined;
private _alg: Alg | string | undefined;
private _serverMetadata: EndpointMetadata | undefined;
private _endpointMetadata: EndpointMetadata | undefined;
private _accessTokenResponse: AccessTokenResponse | undefined;

private constructor(
Expand Down Expand Up @@ -92,10 +93,10 @@ export class OpenID4VCIClient {

public async retrieveServerMetadata(): Promise<EndpointMetadata> {
this.assertIssuerData();
if (!this._serverMetadata) {
this._serverMetadata = await MetadataClient.retrieveAllMetadataFromCredentialOffer(this._credentialOffer);
if (!this._endpointMetadata) {
this._endpointMetadata = await MetadataClient.retrieveAllMetadataFromCredentialOffer(this._credentialOffer);
}
return this._serverMetadata;
return this._endpointMetadata;
}

public createAuthorizationRequestUrl({
Expand All @@ -112,10 +113,10 @@ export class OpenID4VCIClient {
throw Error('Please provide a scope or authorization_details');
}
// todo: handling this because of the support for v1_0-08
if (this._serverMetadata && this._serverMetadata.openid4vci_metadata && 'authorization_endpoint' in this._serverMetadata.openid4vci_metadata) {
this._serverMetadata.authorization_endpoint = this._serverMetadata.openid4vci_metadata.authorization_endpoint as string;
if (this._endpointMetadata && this._endpointMetadata.issuerMetadata && 'authorization_endpoint' in this._endpointMetadata.issuerMetadata) {
this._endpointMetadata.authorization_endpoint = this._endpointMetadata.issuerMetadata.authorization_endpoint as string;
}
if (!this._serverMetadata?.authorization_endpoint) {
if (!this._endpointMetadata?.authorization_endpoint) {
throw Error('Server metadata does not contain authorization endpoint');
}

Expand All @@ -136,7 +137,7 @@ export class OpenID4VCIClient {
} as AuthorizationRequestV1_0_09;

return convertJsonToURI(queryObj, {
baseUrl: this._serverMetadata.authorization_endpoint,
baseUrl: this._endpointMetadata.authorization_endpoint,
uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details'],
});
}
Expand All @@ -158,7 +159,7 @@ export class OpenID4VCIClient {
// Authorization servers supporting PAR SHOULD include the URL of their pushed authorization request endpoint in their authorization server metadata document
// Note that the presence of pushed_authorization_request_endpoint is sufficient for a client to determine that it may use the PAR flow.
// What happens if it doesn't ???
if (!this._serverMetadata?.openid4vci_metadata?.pushed_authorization_request_endpoint) {
if (!this._endpointMetadata?.issuerMetadata || !('pushed_authorization_request_endpoint' in this._endpointMetadata.issuerMetadata)) {
throw Error('Server metadata does not contain pushed authorization request endpoint');
}

Expand All @@ -177,7 +178,8 @@ export class OpenID4VCIClient {
redirect_uri: redirectUri,
scope: scope,
};
return await formPost(this._serverMetadata.openid4vci_metadata.pushed_authorization_request_endpoint, JSON.stringify(queryObj));
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return await formPost(this._endpointMetadata.issuerMetadata.pushed_authorization_request_endpoint!, JSON.stringify(queryObj));
}

public handleAuthorizationDetails(authorizationDetails?: AuthDetails | AuthDetails[]): AuthDetails | AuthDetails[] | undefined {
Expand All @@ -191,15 +193,15 @@ export class OpenID4VCIClient {
return authorizationDetails;
}
private handleLocations(authorizationDetails: AuthDetails) {
if (authorizationDetails && (this.serverMetadata.openid4vci_metadata?.authorization_server || this.serverMetadata.authorization_endpoint)) {
if (authorizationDetails && (this.endpointMetadata.issuerMetadata?.authorization_server || this.endpointMetadata.authorization_endpoint)) {
if (authorizationDetails.locations) {
if (Array.isArray(authorizationDetails.locations)) {
(authorizationDetails.locations as string[]).push(this.serverMetadata.issuer);
(authorizationDetails.locations as string[]).push(this.endpointMetadata.issuer);
} else {
authorizationDetails.locations = [authorizationDetails.locations as string, this.serverMetadata.issuer];
authorizationDetails.locations = [authorizationDetails.locations as string, this.endpointMetadata.issuer];
}
} else {
authorizationDetails.locations = this.serverMetadata.issuer;
authorizationDetails.locations = this.endpointMetadata.issuer;
}
}
return authorizationDetails;
Expand Down Expand Up @@ -227,7 +229,7 @@ export class OpenID4VCIClient {

const response = await accessTokenClient.acquireAccessToken({
credentialOffer: this.credentialOffer,
metadata: this._serverMetadata,
metadata: this._endpointMetadata,
pin,
codeVerifier,
code,
Expand All @@ -238,15 +240,15 @@ export class OpenID4VCIClient {
if (response.errorBody) {
debug(`Access token error:\r\n${response.errorBody}`);
throw Error(
`Retrieving an access token from ${this._serverMetadata?.token_endpoint} for issuer ${this.getIssuer()} failed with status: ${
`Retrieving an access token from ${this._endpointMetadata?.token_endpoint} for issuer ${this.getIssuer()} failed with status: ${
response.origResponse.status
}`
);
} else if (!response.successBody) {
debug(`Access token error. No succes body`);
throw Error(
`Retrieving an access token from ${
this._serverMetadata?.token_endpoint
this._endpointMetadata?.token_endpoint
} for issuer ${this.getIssuer()} failed as there was no success response body`
);
}
Expand Down Expand Up @@ -280,11 +282,11 @@ export class OpenID4VCIClient {

const requestBuilder = CredentialRequestClientBuilderV1_0_09.fromCredentialOffer({
credentialOffer: this.credentialOffer,
metadata: this.serverMetadata,
metadata: this.endpointMetadata,
});
requestBuilder.withToken(this.accessTokenResponse.access_token);
if (this.serverMetadata?.openid4vci_metadata) {
const metadata = this.serverMetadata.openid4vci_metadata;
if (this.endpointMetadata?.issuerMetadata) {
const metadata = this.endpointMetadata.issuerMetadata;
const types = Array.isArray(credentialType) ? credentialType : [credentialType];
if (metadata.credentials_supported && Array.isArray(metadata.credentials_supported)) {
for (const type of types) {
Expand Down Expand Up @@ -327,23 +329,23 @@ export class OpenID4VCIClient {
if (response.errorBody) {
debug(`Credential request error:\r\n${response.errorBody}`);
throw Error(
`Retrieving a credential from ${this._serverMetadata?.credential_endpoint} for issuer ${this.getIssuer()} failed with status: ${
`Retrieving a credential from ${this._endpointMetadata?.credential_endpoint} for issuer ${this.getIssuer()} failed with status: ${
response.origResponse.status
}`
);
} else if (!response.successBody) {
debug(`Credential request error. No success body`);
throw Error(
`Retrieving a credential from ${
this._serverMetadata?.credential_endpoint
this._endpointMetadata?.credential_endpoint
} for issuer ${this.getIssuer()} failed as there was no success response body`
);
}
return response.successBody;
}

getCredentialsSupported(restrictToInitiationTypes: boolean, supportedType?: string): CredentialSupported[] {
const credentialsSupported = this.serverMetadata?.openid4vci_metadata?.credentials_supported;
const credentialsSupported = this.endpointMetadata?.issuerMetadata?.credentials_supported;
if (!credentialsSupported) {
return [];
} else if (!restrictToInitiationTypes) {
Expand Down Expand Up @@ -403,10 +405,10 @@ export class OpenID4VCIClient {
return this._credentialOffer;
}

public get serverMetadata(): EndpointMetadata {
public get endpointMetadata(): EndpointMetadata {
this.assertServerMetadata();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this._serverMetadata!;
return this._endpointMetadata!;
}

get kid(): string {
Expand Down Expand Up @@ -440,19 +442,19 @@ export class OpenID4VCIClient {

public getIssuer(): string {
this.assertIssuerData();
return this._serverMetadata ? this.serverMetadata.issuer : this.getIssuer();
return this._endpointMetadata ? this.endpointMetadata.issuer : this.getIssuer();
}

public getAccessTokenEndpoint(): string {
this.assertIssuerData();
return this.serverMetadata
? this.serverMetadata.token_endpoint
return this.endpointMetadata
? this.endpointMetadata.token_endpoint
: AccessTokenClient.determineTokenURL({ issuerOpts: { issuer: this.getIssuer() } });
}

public getCredentialEndpoint(): string {
this.assertIssuerData();
return this.serverMetadata ? this.serverMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
return this.endpointMetadata ? this.endpointMetadata.credential_endpoint : `${this.getIssuer()}/credential`;
}

private assertIssuerData(): void {
Expand All @@ -462,7 +464,7 @@ export class OpenID4VCIClient {
}

private assertServerMetadata(): void {
if (!this._serverMetadata) {
if (!this._endpointMetadata) {
throw Error('No server metadata');
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/__tests__/IT.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('OID4VCI-Client should', () => {
async function assertionOfsucceedWithAFullFlowWithClient(client: OpenID4VCIClient) {
expect(client.flowType).toEqual(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW);
expect(client.credentialOffer).toBeDefined();
expect(client.serverMetadata).toBeDefined();
expect(client.endpointMetadata).toBeDefined();
expect(client.getIssuer()).toEqual('https://issuer.research.identiproof.io');
expect(client.getCredentialEndpoint()).toEqual('https://issuer.research.identiproof.io/credential');
expect(client.getAccessTokenEndpoint()).toEqual('https://auth.research.identiproof.io/oauth2/token');
Expand Down
10 changes: 5 additions & 5 deletions packages/client/lib/__tests__/MetadataClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('MetadataClient with IdentiProof Issuer should', () => {
const metadata = await MetadataClient.retrieveAllMetadata(IDENTIPROOF_ISSUER_URL);
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
expect(metadata.token_endpoint).toEqual('https://auth.research.identiproof.io/oauth2/token');
expect(metadata.openid4vci_metadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
expect(metadata.issuerMetadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
});

it('succeed with OID4VCI and separate AS metadata from Initiation', async () => {
Expand All @@ -42,7 +42,7 @@ describe('MetadataClient with IdentiProof Issuer should', () => {
const metadata = await MetadataClient.retrieveAllMetadata(getIssuerFromCredentialOfferPayload(initiation.request));
expect(metadata.credential_endpoint).toEqual('https://issuer.research.identiproof.io/credential');
expect(metadata.token_endpoint).toEqual('https://auth.research.identiproof.io/oauth2/token');
expect(metadata.openid4vci_metadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
expect(metadata.issuerMetadata).toEqual(IDENTIPROOF_OID4VCI_METADATA);
});

it('Fail without OID4VCI and only AS metadata (no credential endpoint)', async () => {
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('Metadataclient with Spruce Issuer should', () => {
const metadata = await MetadataClient.retrieveAllMetadata(SPRUCE_ISSUER_URL);
expect(metadata.credential_endpoint).toEqual('https://ngi-oidc4vci-test.spruceid.xyz/credential');
expect(metadata.token_endpoint).toEqual('https://ngi-oidc4vci-test.spruceid.xyz/token');
expect(metadata.openid4vci_metadata).toEqual(SPRUCE_OID4VCI_METADATA);
expect(metadata.issuerMetadata).toEqual(SPRUCE_OID4VCI_METADATA);
});

it('Fail without OID4VCI', async () => {
Expand All @@ -155,7 +155,7 @@ describe('Metadataclient with Danubetech should', () => {
const metadata = await MetadataClient.retrieveAllMetadata(DANUBE_ISSUER_URL);
expect(metadata.credential_endpoint).toEqual('https://oidc4vc.uniissuer.io/credential');
expect(metadata.token_endpoint).toEqual('https://oidc4vc.uniissuer.io/token');
expect(metadata.openid4vci_metadata).toEqual(DANUBE_OIDC_METADATA);
expect(metadata.issuerMetadata).toEqual(DANUBE_OIDC_METADATA);
});

it('Fail without OID4VCI', async () => {
Expand All @@ -182,7 +182,7 @@ describe('Metadataclient with Walt-id should', () => {
const metadata = await MetadataClient.retrieveAllMetadata(WALT_ISSUER_URL);
expect(metadata.credential_endpoint).toEqual('https://jff.walt.id/issuer-api/oidc/credential');
expect(metadata.token_endpoint).toEqual('https://jff.walt.id/issuer-api/oidc/token');
expect(metadata.openid4vci_metadata).toEqual(WALT_OID4VCI_METADATA);
expect(metadata.issuerMetadata).toEqual(WALT_OID4VCI_METADATA);
});

it('Fail without OID4VCI', async () => {
Expand Down
12 changes: 6 additions & 6 deletions packages/client/lib/__tests__/OpenID4VCIClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('OpenID4VCIClient should', () => {
it('should create successfully construct an authorization request url', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata?.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
const url = client.createAuthorizationRequestUrl({
clientId: 'test-client',
codeChallengeMethod: CodeChallengeMethod.SHA256,
Expand Down Expand Up @@ -51,7 +51,7 @@ describe('OpenID4VCIClient should', () => {
it("injects 'openid' as the first scope if not provided", async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata?.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

const url = client.createAuthorizationRequestUrl({
clientId: 'test-client',
Expand All @@ -69,7 +69,7 @@ describe('OpenID4VCIClient should', () => {
it('throw an error if no scope and no authorization_details is provided', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata?.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

expect(() => {
client.createAuthorizationRequestUrl({
Expand All @@ -83,7 +83,7 @@ describe('OpenID4VCIClient should', () => {
it('create an authorization request url with authorization_details array property', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

expect(
client.createAuthorizationRequestUrl({
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('OpenID4VCIClient should', () => {
it('create an authorization request url with authorization_details object property', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

expect(
client.createAuthorizationRequestUrl({
Expand All @@ -138,7 +138,7 @@ describe('OpenID4VCIClient should', () => {
it('create an authorization request url with authorization_details and scope', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client._serverMetadata.openid4vci_metadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;
client._endpointMetadata.issuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`;

expect(
client.createAuthorizationRequestUrl({
Expand Down
Loading

0 comments on commit 24f98e7

Please sign in to comment.