Skip to content

Commit

Permalink
feat: update handling of OIDC auth requests (dynamic presentation sub…
Browse files Browse the repository at this point in the history
…mission creation) (#658)
  • Loading branch information
martines3000 authored Jan 15, 2025
1 parent cdaa99e commit 413bc9c
Show file tree
Hide file tree
Showing 13 changed files with 6,968 additions and 1,012 deletions.
6 changes: 6 additions & 0 deletions .changeset/fast-rats-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@blockchain-lab-um/oidc-client-plugin": minor
"@blockchain-lab-um/masca": minor
---

Update handling of oidc auth requests.
5 changes: 5 additions & 0 deletions .changeset/shaggy-humans-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blockchain-lab-um/dapp": patch
---

Resolve missing intl translation
4 changes: 2 additions & 2 deletions libs/oidc/client-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dependencies": {
"@blockchain-lab-um/oidc-types": "0.1.0-beta.0",
"@blockchain-lab-um/utils": "1.4.0-beta.1",
"@sphereon/pex": "^2.0.1",
"@sphereon/pex": "^3.3.3",
"@veramo/core": "6.0.0",
"@veramo/utils": "6.0.0",
"cross-fetch": "^4.0.0",
Expand All @@ -31,7 +31,7 @@
"qs": "^6.11.2"
},
"devDependencies": {
"@sphereon/ssi-types": "^0.11.0",
"@sphereon/ssi-types": "0.22.0",
"@types/qs": "^6.9.12",
"@vitest/coverage-v8": "1.6.0",
"jest-extended": "4.0.2",
Expand Down
199 changes: 50 additions & 149 deletions libs/oidc/client-plugin/src/agent/client-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import {
type Result,
ResultObject,
qsCustomDecoder,
uint8ArrayToHex,
} from '@blockchain-lab-um/utils';
import { PEX } from '@sphereon/pex';
import type { IVerifiableCredential } from '@sphereon/ssi-types';
import { PEX, PresentationSubmissionLocation } from '@sphereon/pex';
import type {
IVerifiableCredential,
OriginalVerifiableCredential,
} from '@sphereon/ssi-types';
import type { IAgentPlugin } from '@veramo/core';
import { bytesToBase64url } from '@veramo/utils';
import { bytesToBase64url, decodeCredentialToObject } from '@veramo/utils';
import { fetch } from 'cross-fetch';
import { sha256 } from 'ethereum-cryptography/sha256.js';
import { decodeJwt } from 'jose';
Expand Down Expand Up @@ -75,7 +77,6 @@ export class OIDCClientPlugin implements IAgentPlugin {
codeVerifier: null,
};

// FIXME: Set proxy to masca.io
public proxyUrl = 'https://masca.io/api/proxy/oidc';

readonly methods: IOIDCClientPlugin = {
Expand Down Expand Up @@ -520,9 +521,9 @@ export class OIDCClientPlugin implements IAgentPlugin {
return ResultObject.error('Nonce is required');
}

if (authorizationRequest.scope !== 'openid') {
return ResultObject.error('Only openid scope is supported');
}
// if (authorizationRequest.scope !== 'openid') {
// return ResultObject.error('Only openid scope is supported');
// }

if (!authorizationRequest.client_id) {
return ResultObject.error('Client id is required');
Expand Down Expand Up @@ -564,20 +565,13 @@ export class OIDCClientPlugin implements IAgentPlugin {
authorizationRequest.presentation_definition = presentationDefinition;
this.current.presentationDefinition = presentationDefinition;
} else {
this.current.presentationDefinition = JSON.parse(
authorizationRequest.presentation_definition as unknown as string
) as unknown as PresentationDefinition;
this.current.presentationDefinition =
typeof authorizationRequest.presentation_definition === 'string'
? (JSON.parse(
authorizationRequest.presentation_definition as unknown as string
) as unknown as PresentationDefinition)
: (authorizationRequest.presentation_definition as unknown as PresentationDefinition);
}

// This is only if we combine the specs with SIOPv2
// if (
// authorizationRequest.id_token_type &&
// authorizationRequest.id_token_type !== 'subject_signed'
// ) {
// return ResultObject.error(
// 'Only subject_signed id token type is supported'
// );
// }
}

this.current.authorizationRequest = authorizationRequest;
Expand All @@ -593,7 +587,7 @@ export class OIDCClientPlugin implements IAgentPlugin {

public async selectCredentials(
args: SelectCredentialsArgs
): Promise<Result<IVerifiableCredential[]>> {
): Promise<Result<OriginalVerifiableCredential[]>> {
const { credentials } = args;

if (!credentials) {
Expand All @@ -607,54 +601,40 @@ export class OIDCClientPlugin implements IAgentPlugin {
return ResultObject.error('Presentation definition not found');
}

const map = new Map<string, IVerifiableCredential>();

const errors: string[] = [];

// FIXME: Workaround, because PEX doesn't work correctly with multiple input descriptors
presentationDefinition.input_descriptors.forEach((inputDescriptor) => {
const presentationDefinitionSplit: PresentationDefinition = {
id: presentationDefinition.id,
format: presentationDefinition.format,
input_descriptors: [inputDescriptor],
};

const { verifiableCredential } = pex.selectFrom(
presentationDefinitionSplit,
credentials
);

if (!verifiableCredential || verifiableCredential.length === 0) {
errors.push(inputDescriptor.id);
} else {
// Add credentials to hash map (unique by hash)
for (const credential of verifiableCredential) {
const hash = uint8ArrayToHex(
sha256(Buffer.from(JSON.stringify(credential)))
// NOTE: We filter out `JwtDecodedVerifiableCredential` | `SdJwtDecodedVerifiableCredential` types
const decodedCredentials = (
credentials
.map((credential) =>
typeof credential === 'string'
? (decodeCredentialToObject(credential) as IVerifiableCredential)
: credential
)
.filter((credential) => {
const castCredentialToVerifiableCredential =
credential as IVerifiableCredential;

return (
castCredentialToVerifiableCredential.proof !== undefined &&
(Array.isArray(castCredentialToVerifiableCredential.proof)
? castCredentialToVerifiableCredential.proof[0].jwt !== undefined
: castCredentialToVerifiableCredential.proof.jwt !== undefined)
);
}) as IVerifiableCredential[]
).map((credential: any) =>
Array.isArray(credential.proof)
? (credential.proof[0].jwt as string)
: (credential.proof.jwt as string)
);

if (!map.has(hash)) {
map.set(hash, credential);
}
}
}
});
const result = pex.selectFrom(presentationDefinition, decodedCredentials);

if (errors.length > 0) {
if (result.areRequiredCredentialsPresent === 'error') {
return ResultObject.error(
`Failed to select credentials for input descriptors: ${errors.join(
', '
)}`
'Not all credential requests could be satisfied'
);
}

const verifiableCredential = Array.from(map.values());

if (!verifiableCredential) {
return ResultObject.error('Failed to select credentials');
}

return ResultObject.success(verifiableCredential);
return ResultObject.success(result.verifiableCredential ?? []);
}

public async createPresentationSubmission(
Expand All @@ -673,92 +653,13 @@ export class OIDCClientPlugin implements IAgentPlugin {
return ResultObject.error('Presentation definition not found');
}

// FIXME: Pex doesn't work even with workarounds
// Hardcoded to work with EBSI Conformance Tests
const presentationSubmission: PresentationSubmission = {
id: window.crypto.randomUUID(),
definition_id: presentationDefinition.id,
descriptor_map: [
{
id: 'same-device-in-time-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'same-device-in-time-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletSameInTime')
)}]`,
},
},
{
id: 'cross-device-in-time-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'cross-device-in-time-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletCrossInTime')
)}]`,
},
},
{
id: 'same-device-deferred-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'same-device-deferred-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletSameDeferred')
)}]`,
},
},
{
id: 'cross-device-deferred-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'cross-device-deferred-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletCrossDeferred')
)}]`,
},
},
{
id: 'same-device-pre_authorised-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'same-device-pre_authorised-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletSamePreAuthorised')
)}]`,
},
},
{
id: 'cross-device-pre_authorised-credential',
path: '$',
format: 'jwt_vp',
path_nested: {
id: 'cross-device-pre_authorised-credential',
format: 'jwt_vc',
path: `$.verifiableCredential[${credentials.findIndex(
(credential: any) =>
credential.type.includes('CTWalletCrossPreAuthorised')
)}]`,
},
},
],
};
const presentationSubmission = pex.presentationSubmissionFrom(
presentationDefinition,
credentials,
{
presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL,
}
);

return ResultObject.success(presentationSubmission);
}
Expand Down
4 changes: 2 additions & 2 deletions libs/oidc/client-plugin/src/types/IOIDCClientPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
TokenResponse,
} from '@blockchain-lab-um/oidc-types';
import type { Result } from '@blockchain-lab-um/utils';
import type { IVerifiableCredential } from '@sphereon/ssi-types';
import type { OriginalVerifiableCredential } from '@sphereon/ssi-types';
import type { IAgentContext, IPluginMethodMap, IResolver } from '@veramo/core';

import type {
Expand Down Expand Up @@ -45,7 +45,7 @@ export interface IOIDCClientPlugin extends IPluginMethodMap {
): Promise<Result<AuthorizationRequest>>;
selectCredentials(
args: SelectCredentialsArgs
): Promise<Result<IVerifiableCredential[]>>;
): Promise<Result<OriginalVerifiableCredential[]>>;
createPresentationSubmission(
args: CreatePresentationSubmissionArgs
): Promise<Result<PresentationSubmission>>;
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"email": "[email protected]",
"url": "https://blockchain-lab.um.si"
},

"scripts": {
"build": "pnpm nx run-many --target=build",
"build:docker": "./scripts/build-docker.sh",
Expand Down Expand Up @@ -80,7 +79,9 @@
"[email protected]": "patches/[email protected]",
"@iden3/[email protected]": "patches/@[email protected]",
"[email protected]": "patches/[email protected]",
"@metamask/snaps-sdk": "patches/@metamask__snaps-sdk.patch"
"@metamask/snaps-sdk": "patches/@metamask__snaps-sdk.patch",
"@astronautlabs/jsonpath": "patches/@astronautlabs__jsonpath.patch",
"escodegen": "patches/escodegen.patch"
},
"allowNonAppliedPatches": false
}
Expand Down
1 change: 1 addition & 0 deletions packages/dapp/src/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@
"title": "Modify Credential"
},
"OIDCAuthView": {
"start": "Start OIDC Authorization Request",
"error": "Failed to finish OIDC Authorization Request!",
"finished": "Authorization Request finished",
"new-scan": "Scan New Code",
Expand Down
8 changes: 5 additions & 3 deletions packages/snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"@metamask/providers": "17.1.2",
"@metamask/snaps-sdk": "6.9.0",
"@metamask/utils": "9.3.0",
"@sphereon/pex": "3.3.3",
"@veramo/core": "6.0.0",
"@veramo/credential-eip712": "6.0.0",
"@veramo/credential-ld": "6.0.0",
Expand Down Expand Up @@ -101,20 +102,21 @@
},
"devDependencies": {
"@ceramicnetwork/streamid": "2.17.0",
"@metamask/snaps-cli": "6.5.0",
"@metamask/snaps-cli": "6.6.0",
"@metamask/snaps-utils": "8.4.1",
"@sphereon/ssi-types": "0.22.0",
"@types/elliptic": "^6.4.18",
"@types/jsonpath": "^0.2.4",
"@types/lodash.clonedeep": "^4.5.9",
"@types/qs": "^6.9.12",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.4",
"@vitest/coverage-v8": "1.6.0",
"desm": "^1.3.1",
"esbuild": "0.21.1",
"jest-extended": "^4.0.2",
"jose": "^5.2.2",
"node-stdlib-browser": "^1.2.0",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.4",
"vite": "^5.2.11",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "1.6.0"
Expand Down
3 changes: 2 additions & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"./files/circuits/credentialAtomicQuerySigV2/circuit_final.zkey",
"./files/circuits/credentialAtomicQuerySigV2/verification_key.json"
],
"shasum": "8z+x2k7IWORaxqEwcU2DDZZDy0IhYelIz+P/nqeRBso="
"shasum": "JodjqtKp5wf+vGLtAv3BWt+r8rflH91hTOkJBq4U+WU="
},
"initialPermissions": {
"endowment:ethereum-provider": {},
Expand All @@ -40,5 +40,6 @@
"snap_manageState": {},
"endowment:webassembly": {}
},
"platformVersion": "6.9.0",
"manifestVersion": "0.1"
}
Loading

0 comments on commit 413bc9c

Please sign in to comment.