Skip to content

Commit

Permalink
feat: address feedback part 2
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Auer <[email protected]>
  • Loading branch information
auer-martin committed Jul 29, 2024
1 parent d5b4b75 commit 01f6d4d
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 41 deletions.
13 changes: 5 additions & 8 deletions packages/client/lib/AccessTokenClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDPoP, CreateDPoPClientOpts } from '@sphereon/common';
import { createDPoP, CreateDPoPClientOpts, getCreateDPoPOptions } from '@sphereon/common';
import {
AccessTokenRequest,
AccessTokenRequestOpts,
Expand Down Expand Up @@ -93,12 +93,9 @@ export class AccessTokenClient {

let dPoP: string | undefined;
if (createDPoPOpts?.dPoPSigningAlgValuesSupported && createDPoPOpts.dPoPSigningAlgValuesSupported.length > 0) {
const htu = requestTokenURL.split('?')[0].split('#')[0];
dPoP = createDPoPOpts
? await createDPoP({ ...createDPoPOpts, jwtPayloadProps: { ...createDPoPOpts.jwtPayloadProps, htu, htm: 'POST' } })
: undefined;
dPoP = createDPoPOpts ? await createDPoP(getCreateDPoPOptions(createDPoPOpts, requestTokenURL)) : undefined;
}
return this.sendAuthCode(requestTokenURL, accessTokenRequest, { dPoP });
return this.sendAuthCode(requestTokenURL, accessTokenRequest, dPoP ? { headers: { dPoP } } : undefined);
}

public async createAccessTokenRequest(opts: Omit<AccessTokenRequestOpts, 'createDPoPOpts'>): Promise<AccessTokenRequest> {
Expand Down Expand Up @@ -235,10 +232,10 @@ export class AccessTokenClient {
private async sendAuthCode(
requestTokenURL: string,
accessTokenRequest: AccessTokenRequest,
opts?: { dPoP?: string },
opts?: { headers?: Record<string, string> },
): Promise<OpenIDResponse<AccessTokenResponse>> {
return await formPost(requestTokenURL, convertJsonToURI(accessTokenRequest, { mode: JsonURIMode.X_FORM_WWW_URLENCODED }), {
customHeaders: { ...(opts?.dPoP && { dpop: opts.dPoP }) },
customHeaders: opts?.headers ? opts.headers : undefined,
});
}

Expand Down
13 changes: 5 additions & 8 deletions packages/client/lib/AccessTokenClientV1_0_11.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDPoP, CreateDPoPClientOpts } from '@sphereon/common';
import { createDPoP, CreateDPoPClientOpts, getCreateDPoPOptions } from '@sphereon/common';
import {
AccessTokenRequest,
AccessTokenRequestOpts,
Expand Down Expand Up @@ -97,13 +97,10 @@ export class AccessTokenClientV1_0_11 {

let dPoP: string | undefined;
if (createDPoPOpts?.dPoPSigningAlgValuesSupported && createDPoPOpts.dPoPSigningAlgValuesSupported.length > 0) {
const htu = requestTokenURL.split('?')[0].split('#')[0];
dPoP = createDPoPOpts
? await createDPoP({ ...createDPoPOpts, jwtPayloadProps: { ...createDPoPOpts.jwtPayloadProps, htu, htm: 'POST' } })
: undefined;
dPoP = createDPoPOpts ? await createDPoP(getCreateDPoPOptions(createDPoPOpts, requestTokenURL)) : undefined;
}

return this.sendAuthCode(requestTokenURL, accessTokenRequest, { dPoP });
return this.sendAuthCode(requestTokenURL, accessTokenRequest, dPoP ? { headers: { dPoP } } : undefined);
}

public async createAccessTokenRequest(opts: Omit<AccessTokenRequestOpts, 'createDPoPOpts'>): Promise<AccessTokenRequest> {
Expand Down Expand Up @@ -219,10 +216,10 @@ export class AccessTokenClientV1_0_11 {
private async sendAuthCode(
requestTokenURL: string,
accessTokenRequest: AccessTokenRequest,
opts?: { dPoP?: string },
opts?: { headers?: Record<string, string> },
): Promise<OpenIDResponse<AccessTokenResponse>> {
return await formPost(requestTokenURL, convertJsonToURI(accessTokenRequest, { mode: JsonURIMode.X_FORM_WWW_URLENCODED }), {
customHeaders: { ...(opts?.dPoP && { dpop: opts.dPoP }) },
customHeaders: opts?.headers ? opts.headers : undefined,
});
}

Expand Down
16 changes: 5 additions & 11 deletions packages/client/lib/CredentialRequestClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDPoP, CreateDPoPClientOpts } from '@sphereon/common';
import { createDPoP, CreateDPoPClientOpts, getCreateDPoPOptions } from '@sphereon/common';
import {
acquireDeferredCredential,
CredentialRequestV1_0_13,
Expand Down Expand Up @@ -108,7 +108,7 @@ export class CredentialRequestClient {

public async acquireCredentialsUsingRequest(
uniformRequest: UniformCredentialRequest,
createDPoPOps?: CreateDPoPClientOpts,
createDPoPOpts?: CreateDPoPClientOpts,
): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
if (this.version() < OpenId4VCIVersion.VER_1_0_13) {
throw new Error('Versions below v1.0.13 (draft 13) are not supported by the V13 credential request client.');
Expand All @@ -124,19 +124,13 @@ export class CredentialRequestClient {
const requestToken: string = this.credentialRequestOpts.token;

let dPoP: string | undefined;
if (createDPoPOps) {
const htu = credentialEndpoint.split('?')[0].split('#')[0];
dPoP = createDPoPOps
? await createDPoP({
...createDPoPOps,
jwtPayloadProps: { ...createDPoPOps.jwtPayloadProps, htu, htm: 'POST', accessToken: requestToken },
})
: undefined;
if (createDPoPOpts) {
dPoP = createDPoPOpts ? await createDPoP(getCreateDPoPOptions(createDPoPOpts, credentialEndpoint, { accessToken: requestToken })) : undefined;
}

let response = (await post(credentialEndpoint, JSON.stringify(request), {
bearerToken: requestToken,
customHeaders: { ...(createDPoPOps && { dpop: dPoP }) },
customHeaders: { ...(createDPoPOpts && { dpop: dPoP }) },
})) as OpenIDResponse<CredentialResponse> & {
access_token: string;
};
Expand Down
20 changes: 7 additions & 13 deletions packages/client/lib/CredentialRequestClientV1_0_11.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDPoP, CreateDPoPClientOpts } from '@sphereon/common';
import { createDPoP, CreateDPoPClientOpts, getCreateDPoPOptions } from '@sphereon/common';
import {
acquireDeferredCredential,
CredentialResponse,
Expand Down Expand Up @@ -65,17 +65,17 @@ export class CredentialRequestClientV1_0_11 {
credentialTypes?: string | string[];
context?: string[];
format?: CredentialFormat | OID4VCICredentialFormat;
createDPoPOptions?: CreateDPoPClientOpts;
createDPoPOpts?: CreateDPoPClientOpts;
}): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
const { credentialTypes, proofInput, format, context } = opts;

const request = await this.createCredentialRequest({ proofInput, credentialTypes, context, format, version: this.version() });
return await this.acquireCredentialsUsingRequest(request, opts.createDPoPOptions);
return await this.acquireCredentialsUsingRequest(request, opts.createDPoPOpts);
}

public async acquireCredentialsUsingRequest(
uniformRequest: UniformCredentialRequest,
createDPoPOptions?: CreateDPoPClientOpts,
createDPoPOpts?: CreateDPoPClientOpts,
): Promise<OpenIDResponse<CredentialResponse> & { access_token: string }> {
const request = getCredentialRequestForVersion(uniformRequest, this.version());
const credentialEndpoint: string = this.credentialRequestOpts.credentialEndpoint;
Expand All @@ -88,19 +88,13 @@ export class CredentialRequestClientV1_0_11 {
const requestToken: string = this.credentialRequestOpts.token;

let dPoP: string | undefined;
if (createDPoPOptions) {
const htu = credentialEndpoint.split('?')[0].split('#')[0];
dPoP = createDPoPOptions
? await createDPoP({
...createDPoPOptions,
jwtPayloadProps: { ...createDPoPOptions.jwtPayloadProps, htu, htm: 'POST', accessToken: requestToken },
})
: undefined;
if (createDPoPOpts) {
dPoP = createDPoPOpts ? await createDPoP(getCreateDPoPOptions(createDPoPOpts, credentialEndpoint, { accessToken: requestToken })) : undefined;
}

let response = (await post(credentialEndpoint, JSON.stringify(request), {
bearerToken: requestToken,
customHeaders: { ...(createDPoPOptions && { dpop: dPoP }) },
customHeaders: { ...(createDPoPOpts && { dpop: dPoP }) },
})) as OpenIDResponse<CredentialResponse> & {
access_token: string;
};
Expand Down
17 changes: 17 additions & 0 deletions packages/common/lib/dpop/DPoP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ export interface CreateDPoPOpts<JwtPayloadProps = CreateDPoPJwtPayloadProps> {

export type CreateDPoPClientOpts = CreateDPoPOpts<Omit<CreateDPoPJwtPayloadProps, 'htm' | 'htu'>>;

export function getCreateDPoPOptions(
createDPoPClientOpts: CreateDPoPClientOpts,
endPointUrl: string,
resourceRequestOpts?: { accessToken: string },
): CreateDPoPOpts {
const htu = endPointUrl.split('?')[0].split('#')[0];
return {
...createDPoPClientOpts,
jwtPayloadProps: {
...createDPoPClientOpts.jwtPayloadProps,
htu,
htm: 'POST',
...(resourceRequestOpts && { accessToken: resourceRequestOpts.accessToken }),
},
};
}

export async function createDPoP(options: CreateDPoPOpts): Promise<string> {
const { createJwtCallback, jwtIssuer, jwtPayloadProps, dPoPSigningAlgValuesSupported } = options;

Expand Down
14 changes: 13 additions & 1 deletion packages/issuer-rest/lib/IssuerTokenEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@ import { NextFunction, Request, Response } from 'express'
*/
export const handleTokenRequest = <T extends object>({
tokenExpiresIn, // expiration in seconds
accessTokenEndpoint,
accessTokenSignerCallback,
accessTokenIssuer,
cNonceExpiresIn, // expiration in seconds
issuer,
interval,
dPoPVerifyJwtCallback,
requireDPoP,
}: Required<Pick<ITokenEndpointOpts, 'accessTokenIssuer' | 'cNonceExpiresIn' | 'interval' | 'accessTokenSignerCallback' | 'tokenExpiresIn'>> & {
issuer: VcIssuer<T>
dPoPVerifyJwtCallback?: DPoPVerifyJwtCallback
requireDPoP?: boolean
// The full URL of the access token endpoint
accessTokenEndpoint?: string
}) => {
return async (request: Request, response: Response) => {
response.set({
Expand All @@ -47,6 +52,13 @@ export const handleTokenRequest = <T extends object>({
}

let dPoPJwk: JWK | undefined
if (requireDPoP && !request.headers.dpop) {
return sendErrorResponse(response, 400, {
error: TokenErrorResponse.invalid_request,
error_description: 'DPoP is required for requesting access tokens',
})
}

if (request.headers.dpop) {
if (!dPoPVerifyJwtCallback) {
return sendErrorResponse(response, 400, {
Expand All @@ -56,7 +68,7 @@ export const handleTokenRequest = <T extends object>({
}

try {
const fullUrl = request.protocol + '://' + request.get('host') + request.originalUrl
const fullUrl = accessTokenEndpoint ?? request.protocol + '://' + request.get('host') + request.originalUrl
dPoPJwk = await verifyDPoP(
{ method: request.method, headers: request.headers, fullUrl },
{
Expand Down

0 comments on commit 01f6d4d

Please sign in to comment.