Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:Sphereon-Opensource/OID4VC into …
Browse files Browse the repository at this point in the history
…develop
  • Loading branch information
auer-martin committed Sep 25, 2024
2 parents 4d8859e + 5ea7ad2 commit f457d69
Show file tree
Hide file tree
Showing 35 changed files with 1,026 additions and 335 deletions.
25 changes: 13 additions & 12 deletions packages/client/lib/AccessTokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,7 @@ export class AccessTokenClient {
const credentialIssuer = opts.credentialIssuer ?? credentialOfferRequest?.credential_offer?.credential_issuer ?? opts.metadata?.issuer;
await createJwtBearerClientAssertion(request, { ...opts, credentialIssuer });

if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
this.assertAlphanumericPin(opts.pinMetadata, pin);
request.user_pin = pin;
request.tx_code = pin;

request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE;
// we actually know it is there because of the isPreAuthCode call
request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL];

return request as AccessTokenRequest;
}

// Prefer AUTHORIZATION_CODE over PRE_AUTHORIZED_CODE_FLOW
if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
request.grant_type = GrantTypes.AUTHORIZATION_CODE;
request.code = code;
Expand All @@ -156,6 +145,18 @@ export class AccessTokenClient {
return request as AccessTokenRequest;
}

if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
this.assertAlphanumericPin(opts.pinMetadata, pin);
request.user_pin = pin;
request.tx_code = pin;

request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE;
// we actually know it is there because of the isPreAuthCode call
request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL];

return request as AccessTokenRequest;
}

throw new Error('Credential offer request follows neither pre-authorized code nor authorization code flow requirements.');
}

Expand Down
22 changes: 11 additions & 11 deletions packages/client/lib/AccessTokenClientV1_0_11.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,7 @@ export class AccessTokenClientV1_0_11 {
}
await createJwtBearerClientAssertion(request, { ...opts, version: OpenId4VCIVersion.VER_1_0_11, credentialIssuer });

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

request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE;
// we actually know it is there because of the isPreAuthCode call
request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL];

return request as AccessTokenRequest;
}

// Prefer AUTHORIZATION_CODE over PRE_AUTHORIZED_CODE_FLOW
if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
request.grant_type = GrantTypes.AUTHORIZATION_CODE;
request.code = code;
Expand All @@ -160,6 +150,16 @@ export class AccessTokenClientV1_0_11 {
return request as AccessTokenRequest;
}

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

request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE;
// we actually know it is there because of the isPreAuthCode call
request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL];

return request as AccessTokenRequest;
}
throw new Error('Credential offer request does not follow neither pre-authorized code nor authorization code flow requirements.');
}

Expand Down
4 changes: 2 additions & 2 deletions packages/client/lib/functions/AccessTokenUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export const createJwtBearerClientAssertion = async (
sub: clientId,
aud: credentialIssuer,
jti: uuidv4(),
exp: Date.now() / 1000 + 60,
iat: Date.now() / 1000 - 60,
exp: Math.floor(Date.now()) / 1000 + 60,
iat: Math.floor(Date.now()) / 1000 - 60,
},
};
const pop = await ProofOfPossessionBuilder.fromJwt({
Expand Down
27 changes: 22 additions & 5 deletions packages/common/lib/jwt/JwtVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ export const getDidJwtVerifier = (jwt: { header: JwtHeader; payload: JwtPayload
return { method: 'did', didUrl: jwt.header.kid, type: type, alg: jwt.header.alg };
};

const getIssuer = (type: JwtType, payload: JwtPayload): string => {
// For 'request-object' the `iss` value is not required so we map the issuer to client_id
if (type === 'request-object') {
if (!payload.client_id) {
throw new Error('Missing required field client_id in request object JWT');
}
return payload.client_id as string;
}

if (typeof payload.iss !== 'string') {
throw new Error(`Received an invalid JWT. '${type}' contains an invalid iss claim or it is missing.`);
}
return payload.iss;
};

export const getX5cVerifier = (jwt: { header: JwtHeader; payload: JwtPayload }, options: { type: JwtType }): X5cJwtVerifier => {
const { type } = options;
if (!jwt.header.x5c) throw new Error(`Received an invalid JWT. Missing x5c header.`);
Expand All @@ -75,11 +90,13 @@ export const getX5cVerifier = (jwt: { header: JwtHeader; payload: JwtPayload },
throw new Error(`Received an invalid JWT.. '${type}' contains an invalid x5c header.`);
}

if (typeof jwt.payload.iss !== 'string') {
throw new Error(`Received an invalid JWT. '${type}' contains an invalid iss claim.`);
}

return { method: 'x5c', x5c: jwt.header.x5c, issuer: jwt.payload.iss, type: type, alg: jwt.header.alg };
return {
method: 'x5c',
x5c: jwt.header.x5c,
issuer: getIssuer(type, jwt.payload),
type: type,
alg: jwt.header.alg,
};
};

export const getJwkVerifier = async (jwt: { header: JwtHeader; payload: JwtPayload }, options: { type: JwtType }): Promise<JwkJwtVerifier> => {
Expand Down
2 changes: 1 addition & 1 deletion packages/did-auth-siop-adapter/lib/did/DIDResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,5 @@ export async function resolveDidDocument(did: string, opts?: ResolveOpts): Promi
// todo: This looks like a bug. It seems that sometimes we get back a DID document directly instead of a did resolution results
return result as unknown as DIDDocument
}
return result.didDocument
return result.didDocument as DIDDocument
}
2 changes: 1 addition & 1 deletion packages/did-auth-siop-adapter/lib/did/DidJWT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export function getSubDidFromPayload(payload: JWTPayload, header?: JWTHeader): s
export function isIssSelfIssued(payload: JWTPayload): boolean {
return (
(payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V1)) ||
payload.iss.includes(ResponseIss.SELF_ISSUED_V2) ||
(payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V2)) ||
payload.iss === payload.sub
)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/oid4vci-common/lib/functions/ProofUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ const createJWT = (mode: PoPMode, jwtProps?: JwtProps, existingJwt?: Jwt): Jwt =
const now = +new Date();
const jwtPayload: Partial<JWTPayload> = {
...(aud && { aud }),
iat: jwt.payload?.iat ?? Math.round(now / 1000 - 60), // Let's ensure we subtract 60 seconds for potential time offsets
exp: jwt.payload?.exp ?? Math.round(now / 1000 + 10 * 60),
iat: jwt.payload?.iat ?? Math.floor(now / 1000) - 60, // Let's ensure we subtract 60 seconds for potential time offsets
exp: jwt.payload?.exp ?? (Math.floor(now / 1000) + 10) * 60,
nonce,
...(client_id && { client_id }),
...(iss && { iss }),
Expand Down
1 change: 1 addition & 0 deletions packages/oid4vci-common/lib/types/Authorization.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ export interface AuthorizationRequestOpts {
redirectUri?: string;
scope?: string;
requestObjectOpts?: RequestObjectOpts;
holderPreferredAuthzFlowTypeOrder?: AuthzFlowType[]
}

export interface AuthorizationResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ describe('create JWT from Request JWT should', () => {
},
],
constraints: {
limit_disclosure: 'required',
//limit_disclosure: 'required',
fields: [
{
path: ['$.issuer.id'],
Expand Down Expand Up @@ -486,7 +486,7 @@ describe('create JWT from Request JWT should', () => {
},
],
constraints: {
limit_disclosure: 'required',
// limit_disclosure: 'required',
fields: [
{
path: ['$.issuer.id'],
Expand Down
Loading

0 comments on commit f457d69

Please sign in to comment.