Skip to content

Commit

Permalink
feat: Add dPop for token and credential endPoint #3035
Browse files Browse the repository at this point in the history
  • Loading branch information
bibash28 committed Nov 29, 2024
1 parent 9ed096b commit a4505eb
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 68 deletions.
42 changes: 42 additions & 0 deletions lib/app/shared/helper_functions/helper_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2232,3 +2232,45 @@ bool useOauthServerAuthEndPoint(ProfileModel profileModel) {

return false;
}

Future<String> getDPopJwt({
required OIDC4VC oidc4vc,
required String url,
required String publicKey,
String? accessToken,
String? nonce,
}) async {
final tokenParameters = TokenParameters(
privateKey: jsonDecode(publicKey) as Map<String, dynamic>,
mediaType: MediaType.dPop,
did: '', // just added as it is required field
clientType:
ClientType.p256JWKThumprint, // just added as it is required field
proofHeaderType: ProofHeaderType.jwk,
clientId: '', // just added as it is required field
);

final jti = const Uuid().v4();
final iat = (DateTime.now().millisecondsSinceEpoch / 1000).round();

final payload = {
'jti': jti,
'htm': 'POST',
'htu': url,
'iat': iat,
};

if (accessToken != null) {
final hash = oidc4vc.sh256Hash(accessToken);
payload['ath'] = hash;
}

if (nonce != null) payload['nonce'] = nonce;

final jwtToken = oidc4vc.generateToken(
payload: payload,
tokenParameters: tokenParameters,
ignoreProofHeaderType: false,
);
return jwtToken;
}
36 changes: 29 additions & 7 deletions lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
clientSecret: clientSecret,
clientAuthentication: customOidc4vcProfile.clientAuthentication,
oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft,
formatsSupported: customOidc4vcProfile.formatsSupported??[],
formatsSupported: customOidc4vcProfile.formatsSupported ?? [],
oAuthClientAttestation: oAuthClientAttestation,
oAuthClientAttestationPop: oAuthClientAttestationPop,
secureAuthorizedFlow: customOidc4vcProfile.pushAuthorizationRequest,
Expand Down Expand Up @@ -1392,6 +1392,9 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
dio: client.dio,
);

final randomKey = generateRandomP256Key();
final publicKeyForDPop = sortedPublcJwk(randomKey);

if (savedAccessToken == null) {
/// get tokendata
final tokenData = oidc4vc.buildTokenData(
Expand Down Expand Up @@ -1423,24 +1426,42 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
}
}

final tokenEndPoint = await oidc4vc.getTokenEndPoint(
issuer: issuer,
oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft,
openIdConfiguration:
OpenIdConfiguration.fromJson(openIdConfigurationData),
dio: client.dio,
useOAuthAuthorizationServerLink:
useOauthServerAuthEndPoint(profileCubit.state.model),
);

String? dPop;

if (customOidc4vcProfile.dpopSupport) {
dPop = await getDPopJwt(
oidc4vc: profileCubit.oidc4vc,
url: tokenEndPoint,
accessToken: savedAccessToken,
nonce: savedNonce,
publicKey: publicKeyForDPop,
);
}

/// get token response
final (
Map<String, dynamic>? tokenResponse,
String? accessToken,
String? cnonce,
List<dynamic>? authorizationDetails,
) = await oidc4vc.getTokenResponse(
issuer: issuer,
authorization: authorization,
oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft,
openIdConfiguration:
OpenIdConfiguration.fromJson(openIdConfigurationData),
tokenEndPoint: tokenEndPoint,
oAuthClientAttestation: oAuthClientAttestation,
oAuthClientAttestationPop: oAuthClientAttestationPop,
dio: client.dio,
useOAuthAuthorizationServerLink:
useOauthServerAuthEndPoint(profileCubit.state.model),
tokenData: tokenData,
dPop: dPop,
);

savedAccessToken = accessToken;
Expand Down Expand Up @@ -1489,6 +1510,7 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
openIdConfiguration:
OpenIdConfiguration.fromJson(openIdConfigurationData),
qrCodeScanCubit: qrCodeScanCubit,
publicKeyForDPop: publicKeyForDPop,
);

if (result == null) {
Expand Down
101 changes: 94 additions & 7 deletions lib/oidc4vc/get_credential.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Future<
required OpenIdConfiguration openIdConfiguration,
required List<dynamic>? authorizationDetails,
required QRCodeScanCubit qrCodeScanCubit,
required String publicKeyForDPop,
}) async {
final privateKey = await fetchPrivateKey(
isEBSI: isEBSI,
Expand Down Expand Up @@ -125,13 +126,14 @@ Future<
}
}

final credentialResponseDataValue =
await profileCubit.oidc4vc.getSingleCredential(
final credentialResponseDataValue = await getSingleCredentialData(
profileCubit: profileCubit,
openIdConfiguration: openIdConfiguration,
accessToken: accessToken,
nonce: nonce,
cnonce: nonce,
dio: Dio(),
credentialData: credentialData,
publicKeyForDPop: publicKeyForDPop,
);

/// update nonce value
Expand Down Expand Up @@ -162,7 +164,7 @@ Future<
issuer: issuer,
kid: kid,
privateKey: privateKey,
formatsSupported: customOidc4vcProfile.formatsSupported??[],
formatsSupported: customOidc4vcProfile.formatsSupported ?? [],
);

if (profileCubit.state.model.isDeveloperMode) {
Expand All @@ -181,13 +183,14 @@ Future<
}
}

final credentialResponseDataValue =
await profileCubit.oidc4vc.getSingleCredential(
final credentialResponseDataValue = await getSingleCredentialData(
profileCubit: profileCubit,
openIdConfiguration: openIdConfiguration,
accessToken: accessToken,
nonce: cnonce,
cnonce: cnonce,
dio: Dio(),
credentialData: credentialData,
publicKeyForDPop: publicKeyForDPop,
);

credentialResponseData.add(credentialResponseDataValue);
Expand All @@ -202,3 +205,87 @@ Future<
format,
);
}

int count = 0;

Future<dynamic> getSingleCredentialData({
required ProfileCubit profileCubit,
required OpenIdConfiguration openIdConfiguration,
required String accessToken,
required String? cnonce,
required Dio dio,
required Map<String, dynamic> credentialData,
required String publicKeyForDPop,
}) async {
final credentialEndpoint =
profileCubit.oidc4vc.readCredentialEndpoint(openIdConfiguration);

final customOidc4vcProfile = profileCubit.state.model.profileSetting
.selfSovereignIdentityOptions.customOidc4vcProfile;
try {
String? dPop;

if (customOidc4vcProfile.dpopSupport) {
dPop = await getDPopJwt(
oidc4vc: profileCubit.oidc4vc,
url: credentialEndpoint,
accessToken: accessToken,
publicKey: publicKeyForDPop,
);
}

final credentialResponseDataValue =
await profileCubit.oidc4vc.getSingleCredential(
openIdConfiguration: openIdConfiguration,
accessToken: accessToken,
nonce: cnonce,
dio: Dio(),
credentialData: credentialData,
credentialEndpoint: credentialEndpoint,
dPop: dPop,
);

return credentialResponseDataValue;
} catch (e) {
if (count == 1) {
count = 0;
rethrow;
}

if (e is DioException &&
e.response != null &&
e.response!.data is Map<String, dynamic> &&
(e.response!.data as Map<String, dynamic>).containsKey('c_nonce')) {
count++;

String? dPop2;

if (customOidc4vcProfile.dpopSupport) {
dPop2 = await getDPopJwt(
oidc4vc: profileCubit.oidc4vc,
url: credentialEndpoint,
accessToken: accessToken,
publicKey: publicKeyForDPop,
);
}

final nonce = e.response!.data['c_nonce'].toString();

final credentialResponseDataValue =
await profileCubit.oidc4vc.getSingleCredential(
openIdConfiguration: openIdConfiguration,
accessToken: accessToken,
nonce: nonce,
dio: dio,
credentialData: credentialData,
credentialEndpoint: credentialEndpoint,
dPop: dPop2,
);
count = 0;
return credentialResponseDataValue;
} else {
count = 0;
rethrow;
}
}
}
3 changes: 3 additions & 0 deletions packages/oidc4vc/lib/src/media_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ enum MediaType {
basic,
walletAttestation,
selectiveDisclosure,
dPop,
}

extension MediaTypeX on MediaType {
Expand All @@ -16,6 +17,8 @@ extension MediaTypeX on MediaType {
return 'wiar+jwt';
case MediaType.selectiveDisclosure:
return 'kb+jwt';
case MediaType.dPop:
return 'dpop+jwt';
}
}
}
Loading

0 comments on commit a4505eb

Please sign in to comment.