Skip to content

Commit

Permalink
feat: Add initial support for SIOPv2 version draft 11 and OID4VP vers…
Browse files Browse the repository at this point in the history
…ion Draft 18
  • Loading branch information
nklomp committed Oct 2, 2023
1 parent ae3e5be commit 6438e0b
Show file tree
Hide file tree
Showing 19 changed files with 235 additions and 385 deletions.
11 changes: 11 additions & 0 deletions generator/schemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,20 @@ const authorizationRequestPayloadVD11 = {
skipTypeCheck: true
};

const authorizationRequestPayloadVD12OID4VPD18 = {
path: '../src/types/SIOP.types.ts',
tsconfig: 'tsconfig.json',
type: 'AuthorizationRequestPayloadVD12OID4VPD18', // Or <type-name> if you want to generate schema for that one type only
schemaId: 'AuthorizationRequestPayloadVD12OID4VPD18Schema',
outputPath: 'src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts',
// outputConstName: 'AuthorizationRequestPayloadSchemaVD11',
skipTypeCheck: true
};

let schemas: Schema[] = [
writeSchema(authorizationRequestPayloadVID1),
writeSchema(authorizationRequestPayloadVD11),
writeSchema(authorizationRequestPayloadVD12OID4VPD18),
// writeSchema(requestOptsConf),
writeSchema(responseOptsConf),
writeSchema(rPRegistrationMetadataPayload),
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
},
"dependencies": {
"@sphereon/did-uni-client": "^0.6.0",
"@sphereon/pex": "^2.1.2",
"@sphereon/pex-models": "^2.1.0",
"@sphereon/ssi-types": "^0.17.4",
"@sphereon/pex": "2.1.3-unstable.1",
"@sphereon/pex-models": "^2.1.1",
"@sphereon/ssi-types": "^0.17.5",
"@sphereon/wellknown-dids-client": "^0.1.3",
"@astronautlabs/jsonpath": "^1.1.2",
"sha.js": "^2.4.11",
Expand Down
17 changes: 12 additions & 5 deletions src/authorization-request/AuthorizationRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { URI } from './URI';
import { CreateAuthorizationRequestOpts, VerifyAuthorizationRequestOpts } from './types';

export class AuthorizationRequest {
private readonly _requestObject: RequestObject;
private readonly _requestObject?: RequestObject;
private readonly _payload: AuthorizationRequestPayload;
private readonly _options: CreateAuthorizationRequestOpts;
private _uri: URI;
Expand Down Expand Up @@ -94,7 +94,7 @@ export class AuthorizationRequest {
}

public async getSupportedVersionsFromPayload(): Promise<SupportedVersion[]> {
const mergedPayload = { ...this.payload, ...(await this.requestObject.getPayload()) };
const mergedPayload = { ...this.payload, ...(await this.requestObject?.getPayload()) };
return authorizationRequestVersionDiscovery(mergedPayload);
}

Expand Down Expand Up @@ -148,13 +148,20 @@ export class AuthorizationRequest {
throw new Error(`${SIOPErrors.BAD_NONCE} payload: ${mergedPayload.nonce}, supplied: ${opts.nonce}`);
}

const discoveryKey = mergedPayload['registration'] || mergedPayload['registration_uri'] ? 'registration' : 'client_metadata';
const registrationPropertyKey = mergedPayload['registration'] || mergedPayload['registration_uri'] ? 'registration' : 'client_metadata';
let registrationMetadataPayload: RPRegistrationMetadataPayload;
if (mergedPayload[discoveryKey] || mergedPayload[`${discoveryKey}_uri`]) {
registrationMetadataPayload = await fetchByReferenceOrUseByValue(mergedPayload[`${discoveryKey}_uri`], mergedPayload[discoveryKey]);
if (mergedPayload[registrationPropertyKey] || mergedPayload[`${registrationPropertyKey}_uri`]) {
registrationMetadataPayload = await fetchByReferenceOrUseByValue(
mergedPayload[`${registrationPropertyKey}_uri`],
mergedPayload[registrationPropertyKey]
);
assertValidRPRegistrationMedataPayload(registrationMetadataPayload);
// TODO: We need to do something with the metadata probably
}
// When the response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present. If the redirect_uri Authorization Request parameter is present when the Response Mode is direct_post, the Wallet MUST return an invalid_request Authorization Response error.
if (mergedPayload.redirect_uri && mergedPayload.response_uri) {
throw new Error(`${SIOPErrors.INVALID_REQUEST}, redirect_uri cannot be used together with response_uri`);
}
await checkWellknownDIDFromRequest(mergedPayload, opts);

// TODO: we need to verify somewhere that if response_mode is direct_post, that the response_uri may be present,
Expand Down
4 changes: 2 additions & 2 deletions src/did/DIDResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function getResolver(opts: ResolveOpts): Resolvable {
throw Error(`No subject syntax types nor did methods configured for DID resolution, but fallback to universal resolver has been disabled`);
}
console.log(
`Falling back to universal resolver as not resolve opts have been provided, or no subject syntax types supported are provided. It is wise to fix this`
`Falling back to universal resolver as no resolve opts have been provided, or no subject syntax types supported are provided. It is wise to fix this`
);
return new UniResolver();
}
Expand All @@ -37,7 +37,7 @@ export function getResolver(opts: ResolveOpts): Resolvable {
throw Error(`No subject syntax types nor did methods configured for DID resolution, but fallback to universal resolver has been disabled`);
}
console.log(
`Falling back to universal resolver as not resolve opts have been provided, or no subject syntax types supported are provided. It is wise to fix this`
`Falling back to universal resolver as no resolve opts have been provided, or no subject syntax types supported are provided. It is wise to fix this`
);
return new UniResolver();
}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/Encodings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function decodeUriAsJson(uri: string) {
if (!uri) {
throw new Error(SIOPErrors.BAD_PARAMS);
}
const queryString = uri.replace(/^([a-zA-Z][a-zA-Z0-9-_]*:\/\/[a-zA-Z0-9-_%:@\.~!$&'()*+,;=]*[?]?)/, '');
const queryString = uri.replace(/^([a-zA-Z][a-zA-Z0-9-_]*:\/\/[a-zA-Z0-9-_%:@.~!$&'()*+,;=]*[?]?)/, '');
if (!queryString) {
throw new Error(SIOPErrors.BAD_PARAMS);
}
Expand Down
21 changes: 18 additions & 3 deletions src/helpers/SIOPSpecVersion.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AuthorizationRequestPayloadVD11Schema, AuthorizationRequestPayloadVID1Schema } from '../schemas';
import { AuthorizationRequestPayload, SupportedVersion } from '../types';
import { AuthorizationRequestPayloadVD12OID4VPD18Schema } from '../schemas/validation/schemaValidation';
import { AuthorizationRequestPayload, ResponseMode, SupportedVersion } from '../types';
import errors from '../types/Errors';

const validateJWTVCPresentationProfile = AuthorizationRequestPayloadVID1Schema;
Expand All @@ -20,7 +21,6 @@ function isJWTVC1Payload(authorizationRequest: AuthorizationRequestPayload) {
'vp_token' in authorizationRequest.claims
);
}

function isID1Payload(authorizationRequest: AuthorizationRequestPayload) {
return (
!authorizationRequest.client_metadata_uri &&
Expand All @@ -33,12 +33,27 @@ function isID1Payload(authorizationRequest: AuthorizationRequestPayload) {
export const authorizationRequestVersionDiscovery = (authorizationRequest: AuthorizationRequestPayload): SupportedVersion[] => {
const versions = [];
const authorizationRequestCopy: AuthorizationRequestPayload = JSON.parse(JSON.stringify(authorizationRequest));
// todo: We could use v11 validation for v12 for now, as we do not differentiate in the schema at this point\
const vd12Validation = AuthorizationRequestPayloadVD12OID4VPD18Schema(authorizationRequestCopy);
if (vd12Validation) {
if (
!authorizationRequestCopy.registration_uri &&
!authorizationRequestCopy.registration &&
!(authorizationRequestCopy.claims && 'vp_token' in authorizationRequestCopy.claims) &&
authorizationRequestCopy.response_mode !== ResponseMode.POST // Post has been replaced by direct post
) {
versions.push(SupportedVersion.SIOPv2_D12_OID4VP_D18);
}
}
const vd11Validation = AuthorizationRequestPayloadVD11Schema(authorizationRequestCopy);
if (vd11Validation) {
if (
!authorizationRequestCopy.registration_uri &&
!authorizationRequestCopy.registration &&
!(authorizationRequest.claims && 'vp_token' in authorizationRequestCopy.claims)
!(authorizationRequestCopy.claims && 'vp_token' in authorizationRequestCopy.claims) &&
!authorizationRequestCopy.client_id_scheme && // introduced after v11
!authorizationRequestCopy.response_uri &&
authorizationRequestCopy.response_mode !== ResponseMode.DIRECT_POST // Direct post was used before v12 oid4vp18
) {
versions.push(SupportedVersion.SIOPv2_D11);
}
Expand Down
11 changes: 3 additions & 8 deletions src/op/OP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,20 +167,15 @@ export class OP {
!response ||
(response.options?.responseMode &&
!(
response.options?.responseMode === ResponseMode.POST ||
response.options?.responseMode === ResponseMode.FORM_POST ||
response.options.responseMode === ResponseMode.POST ||
response.options.responseMode === ResponseMode.FORM_POST ||
response.options.responseMode === ResponseMode.DIRECT_POST
))
) {
throw new Error(SIOPErrors.BAD_PARAMS);
}
// TODO: there's probably a better place to check this
// We need to return a BAD_REQUEST error
if (response.options.redirectUri && response.options.responseUri) {
throw new Error(SIOPErrors.BAD_PARAMS);
}

const payload = await response.payload;
const payload = response.payload;
const idToken = await response.idToken?.payload();
const responseUri = authorizationResponse.responseURI || idToken?.aud;
if (!responseUri) {
Expand Down
2 changes: 1 addition & 1 deletion src/op/OPBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class OPBuilder {
expiresIn?: number;
issuer?: IIssuerId | ResponseIss;
resolvers: Map<string, Resolvable> = new Map<string, Resolvable>();
responseMode?: ResponseMode = ResponseMode.POST;
responseMode?: ResponseMode = ResponseMode.DIRECT_POST;
responseRegistration?: Partial<ResponseRegistrationOpts> = {};
customResolver?: Resolvable;
signature?: InternalSignature | ExternalSignature | SuppliedSignature;
Expand Down
2 changes: 1 addition & 1 deletion src/request-object/Payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const createRequestObjectPayload = async (opts: CreateAuthorizationReques
//TODO implement /.well-known/openid-federation support in the OP side to resolve the client_id (URL) and retrieve the metadata
client_id: clientId ?? opts.requestObject.signature.did,
redirect_uri: payload.redirect_uri,
response_mode: payload.response_mode ?? ResponseMode.POST,
response_mode: payload.response_mode ?? ResponseMode.DIRECT_POST,
...(payload.id_token_hint && { id_token_hint: payload.id_token_hint }),
registration_uri: registration.clientMetadataOpts.reference_uri,
nonce: getNonce(state, payload.nonce),
Expand Down
2 changes: 1 addition & 1 deletion src/request-object/RequestObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class RequestObject {
}

public static async fromJwt(requestObjectJwt: RequestObjectJwt) {
return new RequestObject(undefined, undefined, requestObjectJwt);
return requestObjectJwt ? new RequestObject(undefined, undefined, requestObjectJwt) : undefined;
}

public static async fromPayload(requestObjectPayload: RequestObjectPayload, authorizationRequestOpts: CreateAuthorizationRequestOpts) {
Expand Down
118 changes: 5 additions & 113 deletions src/schemas/AuthorizationRequestPayloadVD11.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ export const AuthorizationRequestPayloadVD11SchemaObj = {
"fragment",
"form_post",
"post",
"query",
"direct_post"
"direct_post",
"query"
]
},
"ClaimPayloadCommon": {
Expand Down Expand Up @@ -617,12 +617,6 @@ export const AuthorizationRequestPayloadVD11SchemaObj = {
"pattern": {
"type": "string"
},
"contains": {
"$ref": "#/definitions/FilterV1Base"
},
"items": {
"$ref": "#/definitions/FilterV1BaseItems"
},
"type": {
"type": "string"
}
Expand All @@ -638,111 +632,6 @@ export const AuthorizationRequestPayloadVD11SchemaObj = {
"string"
]
},
"FilterV1Base": {
"type": "object",
"properties": {
"const": {
"$ref": "#/definitions/OneOfNumberString"
},
"enum": {
"type": "array",
"items": {
"$ref": "#/definitions/OneOfNumberString"
}
},
"exclusiveMinimum": {
"$ref": "#/definitions/OneOfNumberString"
},
"exclusiveMaximum": {
"$ref": "#/definitions/OneOfNumberString"
},
"format": {
"type": "string"
},
"minLength": {
"type": "number"
},
"maxLength": {
"type": "number"
},
"minimum": {
"$ref": "#/definitions/OneOfNumberString"
},
"maximum": {
"$ref": "#/definitions/OneOfNumberString"
},
"not": {
"type": "object"
},
"pattern": {
"type": "string"
},
"contains": {
"$ref": "#/definitions/FilterV1Base"
},
"items": {
"$ref": "#/definitions/FilterV1BaseItems"
},
"type": {
"type": "string"
}
},
"additionalProperties": false
},
"FilterV1BaseItems": {
"type": "object",
"properties": {
"const": {
"$ref": "#/definitions/OneOfNumberString"
},
"enum": {
"type": "array",
"items": {
"$ref": "#/definitions/OneOfNumberString"
}
},
"exclusiveMinimum": {
"$ref": "#/definitions/OneOfNumberString"
},
"exclusiveMaximum": {
"$ref": "#/definitions/OneOfNumberString"
},
"format": {
"type": "string"
},
"minLength": {
"type": "number"
},
"maxLength": {
"type": "number"
},
"minimum": {
"$ref": "#/definitions/OneOfNumberString"
},
"maximum": {
"$ref": "#/definitions/OneOfNumberString"
},
"not": {
"type": "object"
},
"pattern": {
"type": "string"
},
"contains": {
"$ref": "#/definitions/FilterV1Base"
},
"items": {
"$ref": "#/definitions/FilterV1BaseItems"
},
"type": {
"type": "string"
}
},
"required": [
"type"
],
"additionalProperties": false
},
"HolderSubject": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -811,6 +700,9 @@ export const AuthorizationRequestPayloadVD11SchemaObj = {
"purpose": {
"type": "string"
},
"format": {
"$ref": "#/definitions/Format"
},
"group": {
"type": "array",
"items": {
Expand Down
Loading

0 comments on commit 6438e0b

Please sign in to comment.