Skip to content

Commit

Permalink
fix: Add back jwt_vc format support for older versions
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Dec 21, 2023
1 parent 13659a7 commit 9f06ab1
Show file tree
Hide file tree
Showing 21 changed files with 489 additions and 59 deletions.
2 changes: 1 addition & 1 deletion packages/callback-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@sphereon/oid4vci-client": "workspace:*",
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/oid4vci-issuer": "workspace:*",
"@sphereon/ssi-types": "0.17.2",
"@sphereon/ssi-types": "0.17.6-unstable.23",
"jose": "^4.10.0"
},
"devDependencies": {
Expand Down
18 changes: 10 additions & 8 deletions packages/client/lib/AccessTokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ export class AccessTokenClient {
public async acquireAccessToken(opts: AccessTokenRequestOpts): Promise<OpenIDResponse<AccessTokenResponse>> {
const { asOpts, pin, codeVerifier, code, redirectUri, metadata } = opts;

const credentialOffer = await assertedUniformCredentialOffer(opts.credentialOffer);
const isPinRequired = this.isPinRequiredValue(credentialOffer.credential_offer);
const issuer = getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) ?? (metadata?.issuer as string);
const credentialOffer = opts.credentialOffer ? await assertedUniformCredentialOffer(opts.credentialOffer) : undefined;
const isPinRequired = credentialOffer && this.isPinRequiredValue(credentialOffer.credential_offer);
const issuer =
opts.credentialIssuer ??
(credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : (metadata?.issuer as string));
if (!issuer) {
throw Error('Issuer required at this point');
}
Expand Down Expand Up @@ -83,14 +85,14 @@ export class AccessTokenClient {

public async createAccessTokenRequest(opts: AccessTokenRequestOpts): Promise<AccessTokenRequest> {
const { asOpts, pin, codeVerifier, code, redirectUri } = opts;
const credentialOfferRequest = await toUniformCredentialOfferRequest(opts.credentialOffer);
const credentialOfferRequest = opts.credentialOffer ? await toUniformCredentialOfferRequest(opts.credentialOffer) : undefined;
const request: Partial<AccessTokenRequest> = {};

if (asOpts?.clientId) {
request.client_id = asOpts.clientId;
}

if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin);
request.user_pin = pin;

Expand All @@ -102,7 +104,7 @@ export class AccessTokenClient {
return request as AccessTokenRequest;
}

if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
request.grant_type = GrantTypes.AUTHORIZATION_CODE;
request.code = code;
request.redirect_uri = redirectUri;
Expand Down Expand Up @@ -243,7 +245,7 @@ export class AccessTokenClient {
}

private throwNotSupportedFlow(): void {
debug(`Only pre-authorized flow supported.`);
throw new Error('Only pre-authorized-code flow is supported');
debug(`Only pre-authorized or authorization code flows supported.`);
throw new Error('Only pre-authorized-code or authorization code flows are supported');
}
}
5 changes: 3 additions & 2 deletions packages/client/lib/CredentialRequestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ export class CredentialRequestClient {
throw new Error(URL_NOT_VALID);
}
debug(`Acquiring credential(s) from: ${credentialEndpoint}`);
debug(`request\n: ${JSON.stringify(request, null, 2)}`);
const requestToken: string = this.credentialRequestOpts.token;
const response: OpenIDResponse<CredentialResponse> = await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken });
debug(`Credential endpoint ${credentialEndpoint} response:\r\n${response}`);
debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`);
return response;
}

Expand Down Expand Up @@ -99,7 +100,7 @@ export class CredentialRequestClient {
: await proofInput.build();

// TODO: we should move format specific logic
if (format === 'jwt_vc_json') {
if (format === 'jwt_vc_json' || format === 'jwt_vc') {
return {
types,
format,
Expand Down
19 changes: 19 additions & 0 deletions packages/client/lib/CredentialRequestClientBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ export class CredentialRequestClientBuilder {
token?: string;
version?: OpenId4VCIVersion;

public static fromCredentialIssuer({
credentialIssuer,
metadata,
version,
credentialTypes,
}: {
credentialIssuer: string;
metadata?: EndpointMetadata;
version?: OpenId4VCIVersion;
credentialTypes: string | string[];
}): CredentialRequestClientBuilder {
const issuer = credentialIssuer;
const builder = new CredentialRequestClientBuilder();
builder.withVersion(version ?? OpenId4VCIVersion.VER_1_0_11);
builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`));
builder.withCredentialType(credentialTypes);
return builder;
}

public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise<CredentialRequestClientBuilder> {
const offer = await CredentialOfferClient.fromURI(uri);
return CredentialRequestClientBuilder.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe('Credential Request Client ', () => {
version: OpenId4VCIVersion.VER_1_0_08,
});
expect(credentialRequest.proof?.jwt?.includes(partialJWT)).toBeTruthy();
expect(credentialRequest.format).toEqual('jwt_vc_json');
expect(credentialRequest.format).toEqual('jwt_vc');
const result = await credReqClient.acquireCredentialsUsingRequest(credentialRequest);
expect(result?.successBody?.credential).toEqual(mockedVC);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/__tests__/data/VciDataFixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ const mockData: VciMockDataStructure = {
url: 'https://jff.walt.id/issuer-api/default/oidc/credential',
request: {
types: ['OpenBadgeCredential'],
format: 'jwt_vc_json',
format: 'jwt_vc',
proof: {
proof_type: 'jwt',
jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOa3NpTENKMWMyVWlPaUp6YVdjaUxDSnJkSGtpT2lKRlF5SXNJbU55ZGlJNkluTmxZM0F5TlRack1TSXNJbmdpT2lKclpuVmpTa0V0VEhKck9VWjBPRmx5TFVkMlQzSmpia3N3YjNkc2RqUlhNblUwU3pJeFNHZHZTVlIzSWl3aWVTSTZJalozY0ZCUE1rOUNRVXBTU0ZFMVRXdEtXVlJaV0dsQlJFUXdOMU5OTlV0amVXcDNYMkUzVUUxWmVGa2lmUSMwIn0.eyJhdWQiOiJodHRwczovL2pmZi53YWx0LmlkL2lzc3Vlci1hcGkvZGVmYXVsdC9vaWRjLyIsImlhdCI6MTY4MTkxMTk0Mi4yMzgsImV4cCI6MTY4MTkxMjYwMi4yMzgsIm5vbmNlIjoiZjA2YTMxMDUtYTJlZC00NGZjLTk1NGItNGEyNTk3MDM0OTNiIiwiaXNzIjoic3BoZXJlb246c3NpLXdhbGxldCIsImp0aSI6IjA1OWM3ODA5LTlmOGYtNGE3ZS1hZDI4YTNhMTNhMGIzNmViIn0.RfiWyybxpe3nkx3b0yIsqDHQtvB1WwhDW4t0X-kijy2dsSfv2cYhSEmAzs1shg7OV4EW8fSzt_Te79xiVl6jCw',
Expand Down
4 changes: 3 additions & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"dependencies": {
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/ssi-types": "0.17.2",
"@sphereon/ssi-types": "0.17.6-unstable.23",
"cross-fetch": "^3.1.8",
"debug": "^4.3.4"
},
Expand All @@ -25,6 +25,7 @@
"@types/node": "^18.17.4",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@sphereon/ssi-sdk-ext.key-utils": "^0.15.1-next.7",
"codecov": "^3.8.3",
"dotenv": "^16.3.1",
"eslint": "^8.46.0",
Expand All @@ -38,6 +39,7 @@
"npm-run-all": "^4.1.5",
"uuid": "^9.0.1",
"@transmute/did-key.js": "^0.3.0-unstable.10",
"@trust/keyto": "^2.0.0-alpha1",
"@types/uuid": "^9.0.6",
"open-cli": "^7.2.0",
"ts-jest": "^29.1.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/common/lib/functions/CredentialOfferUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export function getTypesFromOffer(credentialOffer: UniformCredentialOfferPayload
return [...prev, curr];
} else if (curr.format === 'jwt_vc_json-ld' || curr.format === 'ldp_vc') {
return [...prev, ...curr.credential_definition.types];
} else if (curr.format === 'jwt_vc_json') {
} else if (curr.format === 'jwt_vc_json' || curr.format === 'jwt_vc') {
return [...prev, ...curr.types];
} else if (curr.format === 'vc+sd-jwt') {
return [...prev, curr.credential_definition.vct];
Expand Down
2 changes: 1 addition & 1 deletion packages/common/lib/functions/CredentialRequestUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getFormatForVersion } from './FormatUtils';

export function getTypesFromRequest(credentialRequest: UniformCredentialRequest, opts?: { filterVerifiableCredential: boolean }) {
let types: string[] = [];
if (credentialRequest.format === 'jwt_vc_json') {
if (credentialRequest.format === 'jwt_vc_json' || credentialRequest.format === 'jwt_vc') {
types = credentialRequest.types;
} else if (credentialRequest.format === 'jwt_vc_json-ld' || credentialRequest.format === 'ldp_vc') {
types = credentialRequest.credential_definition.types;
Expand Down
2 changes: 1 addition & 1 deletion packages/common/lib/functions/FormatUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function getUniformFormat(format: string | OID4VCICredentialFormat | Cred

// Older formats
if (format === 'jwt_vc' || format === 'jwt') {
return 'jwt_vc_json';
return 'jwt_vc';
}
if (format === 'ldp_vc' || format === 'ldp') {
return 'ldp_vc';
Expand Down
10 changes: 5 additions & 5 deletions packages/common/lib/functions/HttpUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const debug = Debug('sphereon:openid4vci:http');
export const getJson = async <T>(
URL: string,
opts?: {
bearerToken?: string;
bearerToken?: (() => Promise<string>) | string;
contentType?: string;
accept?: string;
customHeaders?: Record<string, string>;
Expand All @@ -22,7 +22,7 @@ export const formPost = async <T>(
url: string,
body: BodyInit,
opts?: {
bearerToken?: string;
bearerToken?: (() => Promise<string>) | string;
contentType?: string;
accept?: string;
customHeaders?: Record<string, string>;
Expand All @@ -36,7 +36,7 @@ export const post = async <T>(
url: string,
body?: BodyInit,
opts?: {
bearerToken?: string;
bearerToken?: (() => Promise<string>) | string;
contentType?: string;
accept?: string;
customHeaders?: Record<string, string>;
Expand All @@ -51,7 +51,7 @@ const openIdFetch = async <T>(
body?: BodyInit,
opts?: {
method?: string;
bearerToken?: string;
bearerToken?: (() => Promise<string>) | string;
contentType?: string;
accept?: string;
customHeaders?: Record<string, string>;
Expand All @@ -60,7 +60,7 @@ const openIdFetch = async <T>(
): Promise<OpenIDResponse<T>> => {
const headers: Record<string, string> = opts?.customHeaders ?? {};
if (opts?.bearerToken) {
headers['Authorization'] = `Bearer ${opts.bearerToken}`;
headers['Authorization'] = `Bearer ${typeof opts.bearerToken === 'function' ? await opts.bearerToken() : opts.bearerToken}`;
}
const method = opts?.method ? opts.method : body ? 'POST' : 'GET';
const accept = opts?.accept ? opts.accept : 'application/json';
Expand Down
7 changes: 6 additions & 1 deletion packages/common/lib/functions/IssuerMetadataUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ export function getSupportedCredential(opts?: {

export function getTypesFromCredentialSupported(credentialSupported: CredentialSupported, opts?: { filterVerifiableCredential: boolean }) {
let types: string[] = [];
if (credentialSupported.format === 'jwt_vc_json' || credentialSupported.format === 'jwt_vc_json-ld' || credentialSupported.format === 'ldp_vc') {
if (
credentialSupported.format === 'jwt_vc_json' ||
credentialSupported.format === 'jwt_vc' ||
credentialSupported.format === 'jwt_vc_json-ld' ||
credentialSupported.format === 'ldp_vc'
) {
types = credentialSupported.types;
} else if (credentialSupported.format === 'vc+sd-jwt') {
types = [credentialSupported.credential_definition.vct];
Expand Down
1 change: 1 addition & 0 deletions packages/common/lib/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './CredentialRequestUtil';
export * from './CredentialOfferUtil';
export * from './Encoding';
export * from './TypeConversionUtils';
export * from './IssuerMetadataUtils';
export * from './FormatUtils';
5 changes: 3 additions & 2 deletions packages/common/lib/types/Authorization.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export interface CommonAuthorizationDetails {
}

export interface AuthorizationDetailsJwtVcJson extends CommonAuthorizationDetails {
format: 'jwt_vc_json';
format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc added for backward compat

/**
* A JSON object containing a list of key value pairs, where the key identifies the claim offered in the Credential.
Expand Down Expand Up @@ -177,7 +177,8 @@ export interface IssuerOpts {
}

export interface AccessTokenRequestOpts {
credentialOffer: UniformCredentialOffer;
credentialOffer?: UniformCredentialOffer;
credentialIssuer?: string;
asOpts?: AuthorizationServerOpts;
metadata?: EndpointMetadata;
codeVerifier?: string; // only required for authorization flow
Expand Down
8 changes: 4 additions & 4 deletions packages/common/lib/types/Generic.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface ImageInfo {
[key: string]: unknown;
}

export type OID4VCICredentialFormat = 'jwt_vc_json' | 'jwt_vc_json-ld' | 'ldp_vc' | 'vc+sd-jwt' /*| 'mso_mdoc'*/; // we do not support mdocs at this point
export type OID4VCICredentialFormat = 'jwt_vc_json' | 'jwt_vc_json-ld' | 'ldp_vc' | 'vc+sd-jwt' | 'jwt_vc'; // jwt_vc is added for backwards compat /*| 'mso_mdoc'*/; // we do not support mdocs at this point

export interface NameAndLocale {
name?: string; // REQUIRED. String value of a display name for the Credential.
Expand Down Expand Up @@ -87,7 +87,7 @@ export interface CredentialSupportedJwtVcJson extends CommonCredentialSupported
types: string[]; // REQUIRED. JSON array designating the types a certain credential type supports
credentialSubject?: IssuerCredentialSubject; // OPTIONAL. A JSON object containing a list of key value pairs, where the key identifies the claim offered in the Credential. The value MAY be a dictionary, which allows to represent the full (potentially deeply nested) structure of the verifiable credential to be issued.
order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet.
format: 'jwt_vc_json';
format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc added for backwards compat
}

export interface SdJwtVcCredentialDefinition {
Expand Down Expand Up @@ -117,7 +117,7 @@ export interface CredentialOfferFormatJwtVcJsonLdAndLdpVc extends CommonCredenti
}

export interface CredentialOfferFormatJwtVcJson extends CommonCredentialOfferFormat {
format: 'jwt_vc_json';
format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc is added for backwards compat
types: string[]; // REQUIRED. JSON array as defined in Appendix E.1.1.2. This claim contains the type values the Wallet shall request in the subsequent Credential Request.
}

Expand Down Expand Up @@ -164,7 +164,7 @@ export interface CommonCredentialRequest {
}

export interface CredentialRequestJwtVcJson extends CommonCredentialRequest {
format: 'jwt_vc_json';
format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc for backwards compat
types: string[];
credentialSubject?: IssuerCredentialSubject;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/common/lib/types/QRCode.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export interface ComponentOptions {
*/
protectors?: boolean;
};
};
}

export interface QRCodeOpts {
/**
Expand Down Expand Up @@ -224,4 +224,4 @@ export interface QRCodeOpts {
* @deafultValue 0.4
*/
dotScale?: number;
};
}
2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build:clean": "tsc --build --clean && tsc --build"
},
"dependencies": {
"@sphereon/ssi-types": "0.17.2",
"@sphereon/ssi-types": "0.17.6-unstable.23",
"cross-fetch": "^3.1.8",
"jwt-decode": "^3.1.2"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ describe('VcIssuer', () => {
})
it('should get state on server side', async () => {
const preAuthCode =
client.credentialOffer.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code']
client.credentialOffer!.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code']
expect(preAuthCode).toBeDefined()

if (preAuthCode) {
Expand Down
4 changes: 2 additions & 2 deletions packages/issuer-rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"dependencies": {
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/oid4vci-issuer": "workspace:*",
"@sphereon/ssi-express-support": "0.17.2",
"@sphereon/ssi-types": "0.17.2",
"@sphereon/ssi-express-support": "0.17.6-unstable.23",
"@sphereon/ssi-types": "0.17.6-unstable.23",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/issuer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/ssi-types": "0.17.2",
"@sphereon/ssi-types": "0.17.6-unstable.23",
"uuid": "^9.0.0"
},
"peerDependencies": {
Expand Down
Loading

0 comments on commit 9f06ab1

Please sign in to comment.