Skip to content

Commit

Permalink
feat: Pass in issuer_state to regular state in auth code flow, so we …
Browse files Browse the repository at this point in the history
…get a better integration with any external OIDC solution
  • Loading branch information
nklomp committed Feb 18, 2025
1 parent 4b09936 commit e6222ff
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 6 deletions.
16 changes: 13 additions & 3 deletions packages/client/lib/AuthorizationCodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export async function createSignedAuthRequestWhenNeeded(requestObject: Record<st
requestObject['request'] = pop.jwt;
}
}

function filterSupportedCredentials(
credentialOffer: CredentialOfferPayloadV1_0_13,
credentialsSupported?: Record<string, CredentialConfigurationSupportedV1_0_13>,
Expand Down Expand Up @@ -200,19 +201,28 @@ export const createAuthorizationRequestUrl = async ({
authorization_details: JSON.stringify(handleAuthorizationDetails(endpointMetadata, authorizationDetails)),
...(redirectUri && { redirect_uri: redirectUri }),
...(client_id && { client_id }),
...(credentialOffer?.issuerState && { issuer_state: credentialOffer.issuerState }),

...(credentialOffer?.issuerState && {
issuer_state: credentialOffer.issuerState,
}),
scope: authorizationRequest.scope,
};

if (credentialOffer?.issuerState) {
/* We also pass it in as state, as this would allow an external AS without integration to return it back to the wallet */
queryObj.state = credentialOffer?.issuerState;
}

if (!parEndpoint && parMode === PARMode.REQUIRE) {
throw Error(`PAR mode is set to required by Authorization Server does not support PAR!`);
} else if (parEndpoint && parMode !== PARMode.NEVER) {
debug(`USING PAR with endpoint ${parEndpoint}`);

const parResponse = await formPost<PushedAuthorizationResponse>(
parEndpoint,
convertJsonToURI(queryObj, {
mode: JsonURIMode.X_FORM_WWW_URLENCODED,
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state'],
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state', 'state'],
}),
{ contentType: 'application/x-www-form-urlencoded', accept: 'application/json' },
);
Expand All @@ -232,7 +242,7 @@ export const createAuthorizationRequestUrl = async ({
debug(`Object that will become query params: ` + JSON.stringify(queryObj, null, 2));
const url = convertJsonToURI(queryObj, {
baseUrl: endpointMetadata.authorization_endpoint,
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state'],
uriTypeProperties: ['client_id', 'request_uri', 'redirect_uri', 'scope', 'authorization_details', 'issuer_state', 'state'],
// arrayTypeProperties: ['authorization_details'],
mode: JsonURIMode.X_FORM_WWW_URLENCODED,
// We do not add the version here, as this always needs to be form encoded
Expand Down
22 changes: 20 additions & 2 deletions packages/issuer-rest/lib/OID4VCIServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
OID4VCICredentialFormat,
QRCodeOpts,
} from '@sphereon/oid4vci-common'
import { CredentialSupportedBuilderV1_13, ITokenEndpointOpts, oidcAccessTokenVerifyCallback, VcIssuer, VcIssuerBuilder } from '@sphereon/oid4vci-issuer'
import {
CredentialSupportedBuilderV1_13,
ITokenEndpointOpts,
oidcAccessTokenVerifyCallback,
VcIssuer,
VcIssuerBuilder,
} from '@sphereon/oid4vci-issuer'
import { ExpressSupport, HasEndpointOpts, ISingleEndpointOpts } from '@sphereon/ssi-express-support'
import express, { Express } from 'express'

Expand Down Expand Up @@ -179,7 +185,19 @@ export class OID4VCIServer {
deleteCredentialOfferEndpoint(this.router, this.issuer, opts?.endpointOpts?.deleteCredentialOfferOpts)
}
getCredentialOfferEndpoint(this.router, this.issuer, opts?.endpointOpts?.getCredentialOfferOpts)
getCredentialEndpoint(this.router, this.issuer, { ...opts?.endpointOpts?.tokenEndpointOpts, baseUrl: this.baseUrl, accessTokenVerificationCallback: opts.endpointOpts?.tokenEndpointOpts?.accessTokenVerificationCallback ?? (this._asClientOpts ? oidcAccessTokenVerifyCallback({clientMetadata: this._asClientOpts, credentialIssuer: this._issuer.issuerMetadata.credential_issuer, authorizationServer: this._issuer.issuerMetadata.authorization_servers![0]}) : undefined)})
getCredentialEndpoint(this.router, this.issuer, {
...opts?.endpointOpts?.tokenEndpointOpts,
baseUrl: this.baseUrl,
accessTokenVerificationCallback:
opts.endpointOpts?.tokenEndpointOpts?.accessTokenVerificationCallback ??
(this._asClientOpts
? oidcAccessTokenVerifyCallback({
clientMetadata: this._asClientOpts,
credentialIssuer: this._issuer.issuerMetadata.credential_issuer,
authorizationServer: this._issuer.issuerMetadata.authorization_servers![0],
})
: undefined),
})
this.assertAccessTokenHandling()
if (!this.isTokenEndpointDisabled(opts?.endpointOpts?.tokenEndpointOpts, opts?.asClientOpts)) {
accessTokenEndpoint(this.router, this.issuer, { ...opts?.endpointOpts?.tokenEndpointOpts, baseUrl: this.baseUrl })
Expand Down
9 changes: 8 additions & 1 deletion packages/issuer/lib/VcIssuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,14 @@ export class VcIssuer {
statusListOpts?: Array<StatusListOpts>
sessionLifeTimeInSec?: number
}): Promise<CreateCredentialOfferURIResult> {
const { offerMode = 'VALUE', correlationId = shortUUID.generate(), credential_configuration_ids, statusListOpts, credentialOfferUri, redirectUri } = opts
const {
offerMode = 'VALUE',
correlationId = shortUUID.generate(),
credential_configuration_ids,
statusListOpts,
credentialOfferUri,
redirectUri,
} = opts
if (offerMode === 'REFERENCE' && !credentialOfferUri) {
return Promise.reject(Error('credentialOfferUri must be supplied for offerMode REFERENCE!'))
}
Expand Down

0 comments on commit e6222ff

Please sign in to comment.