Skip to content

Commit 9672a64

Browse files
authored
Add presentation profiles selection (#217)
* Add presentation-options component * Migrate to initTransaction v2 * Add authorization request URI handling and update presentation options component * Refactor presentation options component layout and styles for improved clarity and usability
1 parent c1d8d12 commit 9672a64

15 files changed

Lines changed: 392 additions & 91 deletions

src/app/core/constants/general.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import { Profile, RequestUriMethod } from "../models/TransactionInitializationRequest";
2+
13
export const ACTIVE_TRANSACTION = 'ACTIVE_TRANSACTION';
24
export const SCHEME = 'scheme';
35
export const DEFAULT_SCHEME = 'haip-vp://';
46
export const ISSUER_CHAIN = 'ISSUER_CHAIN';
7+
8+
export const DefaultRequestUriMethod: RequestUriMethod = 'get';
9+
export const DefaultProfile: Profile = 'openid4vp';
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { RequestUriMethod } from "./TransactionInitializationRequest"
2+
13
export type InitializedTransaction = {
24
client_id: string,
35
request_uri: string,
4-
request_uri_method: 'get' | 'post',
5-
transaction_id: string
6+
request_uri_method: RequestUriMethod,
7+
transaction_id: string,
8+
authorization_request_uri: string
69
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { DCQL } from './dcql/DCQL';
22

3+
export type RequestUriMethod = 'get' | 'post';
4+
export type Profile = 'haip' | 'openid4vp';
5+
36
export type TransactionInitializationRequest = {
47
nonce: string;
5-
request_uri_method: 'get' | 'post';
8+
request_uri_method: RequestUriMethod;
69
dcql_query: DCQL;
710
issuer_chain?: string;
11+
profile: Profile;
12+
authorization_request_uri: string;
813
};
914

1015
export type PresentationQuery = DCQL;

src/app/core/services/dcql-service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
AttributeSelectionMethod,
99
} from '@app/features/presentation-request-preparation/models/AttestationSelection';
1010
import { v4 as uuidv4 } from 'uuid';
11-
import { TransactionInitializationRequest } from '../models/TransactionInitializationRequest';
11+
import { Profile, RequestUriMethod, TransactionInitializationRequest } from '../models/TransactionInitializationRequest';
1212
import {
1313
MsoMdocAttestation,
1414
SdJwtVcAttestation,
@@ -21,7 +21,9 @@ export class DCQLService {
2121
dcqlPresentationRequest(
2222
selectedAttestations: AttestationSelection[],
2323
selectedAttributes: { [id: string]: string[] },
24-
selectedRequestUriMethod: 'get' | 'post',
24+
selectedRequestUriMethod: RequestUriMethod,
25+
selectedProfile: Profile,
26+
authorizationRequestUri: string,
2527
issuerChain?: string
2628
): TransactionInitializationRequest {
2729
let dcqlQueries: CredentialQuery[] = selectedAttestations.map(
@@ -44,6 +46,8 @@ export class DCQLService {
4446
nonce: uuidv4(),
4547
request_uri_method: selectedRequestUriMethod,
4648
issuer_chain: issuerChain,
49+
profile: selectedProfile,
50+
authorization_request_uri: authorizationRequestUri
4751
};
4852
}
4953

src/app/core/services/verifier-endpoint.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {ActiveTransaction} from "@core/models/ActiveTransaction";
1414
import { SessionStorageService } from './session-storage.service';
1515

1616
const SAME_DEVICE_UI_RE_ENTRY_URL = '/get-wallet-code?response_code={RESPONSE_CODE}';
17-
const PRESENTATIONS_ENDPOINT = 'ui/presentations';
17+
const PRESENTATIONS_ENDPOINT = 'ui/presentations/v2';
1818
const VALIDATE_SD_JWT_VC_PRESENTATION_ENDPOINT = 'utilities/validations/sdJwtVc';
1919

2020
@Injectable()

src/app/features/invoke-wallet/components/qr-code/qr-code.component.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class QrCodeComponent implements OnInit, OnDestroy {
5050
transaction!: ActiveTransaction;
5151

5252
deepLinkTxt!: string;
53-
scheme!: string;
53+
5454
qrCodeDownloadLink!: SafeUrl;
5555
readonly dialog!: MatDialog;
5656

@@ -69,12 +69,6 @@ export class QrCodeComponent implements OnInit, OnDestroy {
6969
this.localStorageService = this.injector.get(LocalStorageService);
7070
this.dialog = this.injector.get(MatDialog);
7171
this.isCrossDevice = this.deviceDetectorService.isDesktop();
72-
73-
if (this.localStorageService.get(constants.SCHEME)) {
74-
this.scheme = this.localStorageService.get(constants.SCHEME) ?? constants.DEFAULT_SCHEME;
75-
} else {
76-
this.scheme = constants.DEFAULT_SCHEME;
77-
}
7872
}
7973

8074
ngOnInit(): void {
@@ -84,7 +78,7 @@ export class QrCodeComponent implements OnInit, OnDestroy {
8478
if (!this.transaction) {
8579
this.navigateService.goHome();
8680
} else {
87-
this.deepLinkTxt = this.buildQrCode(this.transaction.initialized_transaction);
81+
this.deepLinkTxt = this.transaction.initialized_transaction.authorization_request_uri;
8882
if (this.isCrossDevice) {
8983
this.pollingRequest(this.transaction.initialized_transaction.transaction_id);
9084
}
@@ -131,10 +125,6 @@ export class QrCodeComponent implements OnInit, OnDestroy {
131125
return concludedTransaction;
132126
}
133127

134-
private buildQrCode(data: { client_id: string, request_uri: string, request_uri_method: 'get' | 'post', transaction_id: string }): string {
135-
return `${this.scheme}?client_id=${encodeURIComponent(data.client_id)}&request_uri=${encodeURIComponent(data.request_uri)}&request_uri_method=${encodeURIComponent(data.request_uri_method)}`;
136-
}
137-
138128
openLogs() {
139129
this.dialog.open(OpenLogsComponent, {
140130
data: {

src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="component-root">
22
<ng-container *ngFor="let selection of attestationsSelection">
3-
<mat-card appearance="outlined">
3+
<mat-card >
44
<mat-card-header>
55
<mat-card-title>{{ nameOf(selection.type) }}</mat-card-title>
66
<mat-card-subtitle>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<mat-card>
2+
<mat-card-header>
3+
<mat-card-title>Presentation Options</mat-card-title>
4+
<mat-card-subtitle>
5+
Configure the options for your presentation request.
6+
</mat-card-subtitle>
7+
</mat-card-header>
8+
9+
<mat-card-content>
10+
<div class="request-options-grid">
11+
<div class="request-options-row">
12+
<div class="request-options-label">
13+
<span>Presentation Profile</span>
14+
<p class="request-options-helper">
15+
Defines the rules and constraints of the presentation
16+
</p>
17+
</div>
18+
<div class="request-options-control">
19+
<mat-button-toggle-group
20+
[formControl]="presentationProfileControl"
21+
(valueChange)="profileChange.emit($event)"
22+
aria-label="Presentation Profile"
23+
>
24+
<mat-button-toggle value="openid4vp">OpenID4VP</mat-button-toggle>
25+
<mat-button-toggle value="haip">HAIP</mat-button-toggle>
26+
</mat-button-toggle-group>
27+
</div>
28+
</div>
29+
30+
<div class="request-options-row">
31+
<div class="request-options-label">
32+
<span>Request URI Method</span>
33+
<p class="request-options-helper">
34+
Choose how the wallet fetches the authorization request
35+
</p>
36+
</div>
37+
<div class="request-options-control">
38+
<mat-button-toggle-group
39+
[formControl]="requestUriMethodControl"
40+
(valueChange)="requestUriMethodChange.emit($event)"
41+
aria-label="Request URI Method"
42+
>
43+
<mat-button-toggle value="get">GET</mat-button-toggle>
44+
<mat-button-toggle value="post">POST</mat-button-toggle>
45+
</mat-button-toggle-group>
46+
</div>
47+
</div>
48+
49+
<div class="request-options-row">
50+
<div class="request-options-label">
51+
<span>Authorization URI Scheme</span>
52+
<p class="request-options-helper">
53+
Specify a custom URI for the authorization request
54+
</p>
55+
</div>
56+
<div class="request-options-control">
57+
<mat-form-field appearance="outline" class="config-option-field">
58+
<mat-label>Authorization URI Scheme</mat-label>
59+
<input
60+
matInput
61+
placeholder="scheme://"
62+
[formControl]="authorizationSchemeControl"
63+
autocomplete="off"
64+
(input)="
65+
authorizationSchemeChange.emit(authorizationSchemeControl.value)
66+
"
67+
/>
68+
</mat-form-field>
69+
</div>
70+
</div>
71+
</div>
72+
</mat-card-content>
73+
</mat-card>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
:host {
2+
display: block;
3+
}
4+
5+
.request-options-grid {
6+
display: flex;
7+
flex-direction: column;
8+
gap: 24px;
9+
padding-top: 16px;
10+
}
11+
12+
.request-options-row {
13+
display: grid;
14+
grid-template-columns: 220px minmax(0, 1fr);
15+
gap: 16px;
16+
}
17+
18+
.request-options-label {
19+
text-align: right;
20+
21+
span {
22+
display: block;
23+
font-weight: 500;
24+
}
25+
}
26+
27+
.request-options-helper {
28+
margin: 4px 0 0;
29+
font-size: 0.85rem;
30+
color: rgba(0, 0, 0, 0.6);
31+
}
32+
33+
.request-options-control {
34+
display: flex;
35+
flex-direction: column;
36+
gap: 8px;
37+
}
38+
39+
@media (max-width: 768px) {
40+
.request-options-row {
41+
grid-template-columns: 1fr;
42+
}
43+
44+
.request-options-label {
45+
text-align: left;
46+
}
47+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { PresentationOptionsComponent } from './presentation-options.component';
4+
5+
describe('PresentationOptionsComponent', () => {
6+
let component: PresentationOptionsComponent;
7+
let fixture: ComponentFixture<PresentationOptionsComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [PresentationOptionsComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(PresentationOptionsComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});

0 commit comments

Comments
 (0)