+
-
+
-
+
- The consent can be used {{ aisConsent.consent.frequencyPerDay }} times per day
+ The consent can be used:
+
+
times per day
+
+
+
+
+
+
+
+ The value must be greater than or equal to 1.
+
+
+ Incorrect date format or date (past dates cannot be used).
+
+
-
+
diff --git a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts
index 15f48bbe93..ea22e5c79c 100644
--- a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts
+++ b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts
@@ -42,6 +42,7 @@ describe('TransactionsConsentReviewComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TransactionsConsentReviewComponent);
component = fixture.componentInstance;
+ component.ngOnInit();
fixture.detectChanges();
consentAuthorizationService = TestBed.inject(UpdateConsentAuthorizationService);
});
@@ -56,6 +57,7 @@ describe('TransactionsConsentReviewComponent', () => {
expect(location.back).toHaveBeenCalled();
});
+ // FIXME Disabled as DateUtil.isDateNotInThePastValidator seem to cause 'undefined' error in control validation
it('should confirm transaction when confirm button is pressed', () => {
consentAuthorizationServiceSpy = spyOn(consentAuthorizationService, 'embeddedUsingPOST').and.returnValue(of());
component.onConfirm();
diff --git a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts
index 05cac4b2ab..3285d5d9c2 100644
--- a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts
+++ b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts
@@ -1,14 +1,16 @@
-import { Component, OnInit } from '@angular/core';
-import { Location } from '@angular/common';
-import { SharedRoutes } from '../../common/shared-routes';
-import { ActivatedRoute, Router } from '@angular/router';
-import { FormBuilder } from '@angular/forms';
-import { SessionService } from '../../../../../common/session.service';
-import { AccountAccessLevel, AisConsentToGrant } from '../../../../common/dto/ais-consent';
-import { StubUtil } from '../../../../../common/utils/stub-util';
-import { ConsentUtil } from '../../../../common/consent-util';
-import { ApiHeaders } from '../../../../../api/api.headers';
-import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from '../../../../../api';
+import {Component, OnInit} from '@angular/core';
+import {Location} from '@angular/common';
+import {SharedRoutes} from '../../common/shared-routes';
+import {ActivatedRoute, Router} from '@angular/router';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {SessionService} from '../../../../../common/session.service';
+import {AccountAccessLevel, AisConsentToGrant} from '../../../../common/dto/ais-consent';
+import {StubUtil} from '../../../../../common/utils/stub-util';
+import {ConsentUtil} from '../../../../common/consent-util';
+import {ApiHeaders} from '../../../../../api/api.headers';
+import {ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest} from '../../../../../api';
+import {DATA_PATTERN, MAX_FREQUENCY_PER_DAY} from '../../../../common/constant/constant';
+import {DateUtil} from '../../../../common/date-util';
@Component({
selector: 'consent-app-transactions-consent-review',
@@ -16,6 +18,9 @@ import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from '
styleUrls: ['./transactions-consent-review.component.scss']
})
export class TransactionsConsentReviewComponent implements OnInit {
+
+ consentReviewForm: FormGroup;
+
constructor(
private location: Location,
private router: Router,
@@ -28,9 +33,9 @@ export class TransactionsConsentReviewComponent implements OnInit {
public static ROUTE = SharedRoutes.REVIEW;
accountAccessLevel = AccountAccessLevel;
+ public actualDate: string;
public finTechName: string;
public aspspName: string;
-
public aisConsent: AisConsentToGrant;
private authorizationId: string;
@@ -41,10 +46,22 @@ export class TransactionsConsentReviewComponent implements OnInit {
this.aspspName = this.sessionService.getBankName(res.authId);
this.finTechName = this.sessionService.getFintechName(res.authId);
this.aisConsent = ConsentUtil.getOrDefault(this.authorizationId, this.sessionService);
+ this.actualDate = DateUtil.getActualDate();
});
+ this.createForm();
}
onConfirm() {
+ if (this.consentReviewForm.invalid) {
+ return;
+ }
+
+ this.aisConsent.consent.recurringIndicator = this.consentReviewForm.value.recurringIndicator;
+ this.aisConsent.consent.validUntil = this.consentReviewForm.value.validUntilDate;
+ this.aisConsent.consent.frequencyPerDay = this.consentReviewForm.value.frequencyPerDay;
+
+ this.sessionService.setConsentObject(this.authorizationId, this.aisConsent);
+
const body = { extras: this.aisConsent.extras } as PsuAuthRequest;
if (this.aisConsent) {
@@ -54,14 +71,13 @@ export class TransactionsConsentReviewComponent implements OnInit {
this.updateConsentAuthorizationService
.embeddedUsingPOST(
this.authorizationId,
- StubUtil.X_XSRF_TOKEN,
StubUtil.X_REQUEST_ID,
this.sessionService.getRedirectCode(this.authorizationId),
body,
'response'
)
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
window.location.href = res.headers.get(ApiHeaders.LOCATION);
});
}
@@ -69,4 +85,34 @@ export class TransactionsConsentReviewComponent implements OnInit {
onBack() {
this.location.back();
}
+
+ private createForm() {
+ this.consentReviewForm = this.formBuilder.group({
+ recurringIndicator: this.aisConsent.consent.recurringIndicator,
+ validUntilDate: [
+ this.aisConsent.consent.validUntil,
+ [
+ Validators.required,
+ Validators.pattern(DATA_PATTERN),
+ DateUtil.isDateNotInThePastValidator()
+ ]
+ ],
+ frequencyPerDay: [
+ this.aisConsent.consent.frequencyPerDay,
+ [
+ Validators.required,
+ Validators.min(1),
+ Validators.max(MAX_FREQUENCY_PER_DAY)
+ ]
+ ]
+ })
+ }
+
+ get validUntilDate() {
+ return this.consentReviewForm.get('validUntilDate')
+ }
+
+ get frequencyPerDay() {
+ return this.consentReviewForm.get('frequencyPerDay')
+ }
}
diff --git a/consent-ui/src/app/ais/result-page/result-page.component.ts b/consent-ui/src/app/ais/result-page/result-page.component.ts
index 5d1ca6f5f6..08ceede21d 100644
--- a/consent-ui/src/app/ais/result-page/result-page.component.ts
+++ b/consent-ui/src/app/ais/result-page/result-page.component.ts
@@ -1,14 +1,13 @@
-import { Component, OnInit } from '@angular/core';
-import { Location } from '@angular/common';
-import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
+import {Component, OnInit} from '@angular/core';
+import {Location} from '@angular/common';
+import {ActivatedRoute, ActivatedRouteSnapshot} from '@angular/router';
-import { StubUtil } from '../../common/utils/stub-util';
-import { AisConsentToGrant } from '../common/dto/ais-consent';
-import { SessionService } from '../../common/session.service';
-import { ConsentUtil } from '../common/consent-util';
-import { ApiHeaders } from '../../api/api.headers';
-import { UpdateConsentAuthorizationService } from '../../api';
-import { AuthStateConsentAuthorizationService, DenyRequest } from '../../api';
+import {StubUtil} from '../../common/utils/stub-util';
+import {AisConsentToGrant} from '../common/dto/ais-consent';
+import {SessionService} from '../../common/session.service';
+import {ConsentUtil} from '../common/consent-util';
+import {ApiHeaders} from '../../api/api.headers';
+import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api';
@Component({
selector: 'consent-app-result-page',
@@ -53,8 +52,6 @@ export class ResultPageComponent implements OnInit {
.denyUsingPOST(
this.authorizationId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
- {} as DenyRequest,
'response'
)
.subscribe((res) => {
@@ -65,7 +62,7 @@ export class ResultPageComponent implements OnInit {
private loadRedirectUri(authId: string, redirectCode: string) {
this.authStateConsentAuthorizationService.authUsingGET(authId, redirectCode, 'response').subscribe((res) => {
console.log(res);
- this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.redirectTo = res.headers.get(ApiHeaders.LOCATION);
});
}
diff --git a/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts b/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts
index 631213e61d..c78e061771 100644
--- a/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts
+++ b/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts
@@ -1,15 +1,15 @@
-import { Component, OnInit } from '@angular/core';
-import { Location } from '@angular/common';
-import { ActivatedRoute } from '@angular/router';
+import {Component, OnInit} from '@angular/core';
+import {Location} from '@angular/common';
+import {ActivatedRoute} from '@angular/router';
-import { AisConsentToGrant } from '../common/dto/ais-consent';
-import { StubUtil } from '../../common/utils/stub-util';
-import { SessionService } from '../../common/session.service';
-import { ConsentUtil } from '../common/consent-util';
-import { ApiHeaders } from '../../api/api.headers';
-import { Action } from '../../common/utils/action';
-import { AuthStateConsentAuthorizationService, DenyRequest, UpdateConsentAuthorizationService } from '../../api';
-import { combineLatest } from 'rxjs';
+import {AisConsentToGrant} from '../common/dto/ais-consent';
+import {StubUtil} from '../../common/utils/stub-util';
+import {SessionService} from '../../common/session.service';
+import {ConsentUtil} from '../common/consent-util';
+import {ApiHeaders} from '../../api/api.headers';
+import {Action} from '../../common/utils/action';
+import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api';
+import {combineLatest} from 'rxjs';
@Component({
selector: 'consent-app-to-aspsp-redirection',
@@ -57,7 +57,7 @@ export class ToAspspRedirectionComponent implements OnInit {
this.authStateConsentAuthorizationService
.authUsingGET(this.authorizationId, this.sessionService.getRedirectCode(this.authorizationId), 'response')
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.redirectTo = res.headers.get(ApiHeaders.LOCATION);
});
}
@@ -67,8 +67,6 @@ export class ToAspspRedirectionComponent implements OnInit {
.denyUsingPOST(
this.authorizationId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
- {} as DenyRequest,
'response'
)
.subscribe((res) => {
diff --git a/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html
new file mode 100644
index 0000000000..29201e5112
--- /dev/null
+++ b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html
@@ -0,0 +1,10 @@
+
+
+
+ Confirm consent on your application
+
+ {{authResponse?.scaMethodSelected.explanation}}
+
+
+
+
diff --git a/consent-ui/src/app/ais/common/account-details/account-details.component.scss b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.scss
similarity index 100%
rename from consent-ui/src/app/ais/common/account-details/account-details.component.scss
rename to consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.scss
diff --git a/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts
new file mode 100644
index 0000000000..9c31723ee6
--- /dev/null
+++ b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts
@@ -0,0 +1,64 @@
+import {Component, OnInit} from '@angular/core';
+import {ActivatedRoute} from "@angular/router";
+import {ApiHeaders} from "../../api/api.headers";
+import {SessionService} from "../../common/session.service";
+import {
+ AuthStateConsentAuthorizationService,
+ ConsentAuth,
+ PsuAuthRequest,
+ UpdateConsentAuthorizationService
+} from '../../api';
+import {StubUtil} from '../../common/utils/stub-util';
+import {delay, repeatWhen, shareReplay, single, switchMap, takeUntil, tap} from "rxjs/operators";
+import {interval, Observable, of, Subject} from "rxjs";
+
+@Component({
+ selector: 'wait-for-decoupled-redirection',
+ templateUrl: './wait-for-decoupled.html',
+ styleUrls: ['./wait-for-decoupled.scss']
+})
+export class WaitForDecoupled implements OnInit {
+ public static ROUTE = 'wait-sca-finalization';
+
+ private readonly POLLING_DELAY_MS = 3000;
+
+ authResponse: ConsentAuth | undefined;
+
+ private authId: string;
+
+ private decoupledCompleted = new Subject();
+
+ constructor(
+ private consentAuthorizationService: UpdateConsentAuthorizationService,
+ private consentStatusService: AuthStateConsentAuthorizationService,
+ private sessionService: SessionService,
+ private activatedRoute: ActivatedRoute
+ ) {
+ const route = this.activatedRoute.snapshot;
+ this.authId = route.parent.params.authId;
+ this.sessionService.setRedirectCode(this.authId, route.queryParams.redirectCode);
+ }
+
+ ngOnInit() {
+ of(true)
+ .pipe(switchMap(_ => this.consentAuthorizationService.embeddedUsingPOST(
+ this.authId,
+ StubUtil.X_REQUEST_ID,
+ this.sessionService.getRedirectCode(this.authId),
+ {} as PsuAuthRequest,
+ 'response'
+ )))
+ .pipe(repeatWhen(completed => completed.pipe(delay(this.POLLING_DELAY_MS))), tap())
+ .pipe(takeUntil(this.decoupledCompleted))
+ .subscribe(res => {
+ if (res.headers.get(ApiHeaders.X_XSRF_TOKEN)) {
+ this.sessionService.setRedirectCode(this.authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
+ }
+ this.authResponse = res.body;
+
+ if (res.status === 202) {
+ window.location.href = res.headers.get(ApiHeaders.LOCATION);
+ }
+ });
+ }
+}
diff --git a/consent-ui/src/app/api-auth/.openapi-generator/VERSION b/consent-ui/src/app/api-auth/.openapi-generator/VERSION
index 078bf8b7dd..ecedc98d1d 100644
--- a/consent-ui/src/app/api-auth/.openapi-generator/VERSION
+++ b/consent-ui/src/app/api-auth/.openapi-generator/VERSION
@@ -1 +1 @@
-4.2.2
\ No newline at end of file
+4.3.1
\ No newline at end of file
diff --git a/consent-ui/src/app/api-auth/README.md b/consent-ui/src/app/api-auth/README.md
index 6fdf5e362f..e0c9192e33 100644
--- a/consent-ui/src/app/api-auth/README.md
+++ b/consent-ui/src/app/api-auth/README.md
@@ -92,6 +92,31 @@ export function apiConfigFactory (): Configuration => {
export class AppModule {}
```
+```
+// configuring providers with an authentication service that manages your access tokens
+import { ApiModule, Configuration } from '';
+
+@NgModule({
+ imports: [ ApiModule ],
+ declarations: [ AppComponent ],
+ providers: [
+ {
+ provide: Configuration,
+ useFactory: (authService: AuthService) => new Configuration(
+ {
+ basePath: environment.apiUrl,
+ accessToken: authService.getAccessToken.bind(authService)
+ }
+ ),
+ deps: [AuthService],
+ multi: false
+ }
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
```
import { DefaultApi } from '';
diff --git a/consent-ui/src/app/api-auth/api.module.ts b/consent-ui/src/app/api-auth/api.module.ts
index 32aa339db8..322fbd1a03 100644
--- a/consent-ui/src/app/api-auth/api.module.ts
+++ b/consent-ui/src/app/api-auth/api.module.ts
@@ -10,12 +10,10 @@ import { PsuAuthenticationAndConsentApprovalService } from './api/psuAuthenticat
imports: [],
declarations: [],
exports: [],
- providers: [
- PsuAuthenticationService,
- PsuAuthenticationAndConsentApprovalService ]
+ providers: []
})
export class ApiModule {
- public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders {
+ public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders
{
return {
ngModule: ApiModule,
providers: [ { provide: Configuration, useFactory: configurationFactory } ]
diff --git a/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts b/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts
index 80a046ee72..c9e8e2571d 100644
--- a/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts
+++ b/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts
@@ -17,9 +17,9 @@ import { HttpClient, HttpHeaders, HttpParams,
import { CustomHttpParameterCodec } from '../encoder';
import { Observable } from 'rxjs';
-import { GeneralError } from '../model/generalError';
-import { LoginResponse } from '../model/loginResponse';
-import { PsuAuthBody } from '../model/psuAuthBody';
+import { GeneralError } from '../model/models';
+import { LoginResponse } from '../model/models';
+import { PsuAuthBody } from '../model/models';
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
@@ -51,6 +51,42 @@ export class PsuAuthenticationService {
+ private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams {
+ if (typeof value === "object" && value instanceof Date === false) {
+ httpParams = this.addToHttpParamsRecursive(httpParams, value);
+ } else {
+ httpParams = this.addToHttpParamsRecursive(httpParams, value, key);
+ }
+ return httpParams;
+ }
+
+ private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams {
+ if (value == null) {
+ return httpParams;
+ }
+
+ if (typeof value === "object") {
+ if (Array.isArray(value)) {
+ (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key));
+ } else if (value instanceof Date) {
+ if (key != null) {
+ httpParams = httpParams.append(key,
+ (value as Date).toISOString().substr(0, 10));
+ } else {
+ throw Error("key may not be null if value is Date");
+ }
+ } else {
+ Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive(
+ httpParams, value[k], key != null ? `${key}.${k}` : k));
+ }
+ } else if (key != null) {
+ httpParams = httpParams.append(key, value);
+ } else {
+ throw Error("key may not be null if value is not object or array");
+ }
+ return httpParams;
+ }
+
/**
* Login user to open-banking
* TBD
@@ -59,10 +95,10 @@ export class PsuAuthenticationService {
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable;
- public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>;
- public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>;
- public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable {
+ public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling login.');
}
@@ -75,11 +111,14 @@ export class PsuAuthenticationService {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (httpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
@@ -94,9 +133,15 @@ export class PsuAuthenticationService {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
+ let responseType: 'text' | 'json' = 'json';
+ if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
+ responseType = 'text';
+ }
+
return this.httpClient.post(`${this.configuration.basePath}/v1/psu/login`,
psuAuthBody,
{
+ responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
@@ -113,10 +158,10 @@ export class PsuAuthenticationService {
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable;
- public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>;
- public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>;
- public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable {
+ public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling registration.');
}
@@ -129,11 +174,14 @@ export class PsuAuthenticationService {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (httpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
@@ -148,9 +196,15 @@ export class PsuAuthenticationService {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
+ let responseType: 'text' | 'json' = 'json';
+ if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
+ responseType = 'text';
+ }
+
return this.httpClient.post(`${this.configuration.basePath}/v1/psu/register`,
psuAuthBody,
{
+ responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
@@ -167,10 +221,10 @@ export class PsuAuthenticationService {
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'body', reportProgress?: boolean): Observable;
- public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'response', reportProgress?: boolean): Observable>;
- public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'events', reportProgress?: boolean): Observable>;
- public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe: any = 'body', reportProgress: boolean = false ): Observable {
+ public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling renewalAuthorizationSessionKey.');
}
@@ -183,18 +237,27 @@ export class PsuAuthenticationService {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (httpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
+ let responseType: 'text' | 'json' = 'json';
+ if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
+ responseType = 'text';
+ }
+
return this.httpClient.get(`${this.configuration.basePath}/v1/psu/ais/${encodeURIComponent(String(authorizationId))}/renewal-authorization-session-key`,
{
+ responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
diff --git a/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts b/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts
index 56f46bb9ad..671854eb6f 100644
--- a/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts
+++ b/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts
@@ -3,7 +3,7 @@
* This API provides PSU login and registration functionality on TPP side.
*
* The version of the OpenAPI document: 1.0.0
- *
+ *
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
@@ -11,15 +11,19 @@
*/
/* tslint:disable:no-unused-variable member-ordering */
-import { Inject, Injectable, Optional } from '@angular/core';
-import { HttpClient, HttpEvent, HttpHeaders, HttpParameterCodec, HttpParams, HttpResponse } from '@angular/common/http';
-import { CustomHttpParameterCodec } from '../encoder';
-import { Observable } from 'rxjs';
-import { LoginResponse } from '../model/loginResponse';
-import { PsuAuthBody } from '../model/psuAuthBody';
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+import { GeneralError } from '../model/models';
+import { LoginResponse } from '../model/models';
+import { PsuAuthBody } from '../model/models';
+
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
-import { BASE_PATH } from '../variables';
-import { Configuration } from '../configuration';
@Injectable({
@@ -47,32 +51,69 @@ export class PsuAuthenticationAndConsentApprovalService {
+ private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams {
+ if (typeof value === "object" && value instanceof Date === false) {
+ httpParams = this.addToHttpParamsRecursive(httpParams, value);
+ } else {
+ httpParams = this.addToHttpParamsRecursive(httpParams, value, key);
+ }
+ return httpParams;
+ }
+
+ private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams {
+ if (value == null) {
+ return httpParams;
+ }
+
+ if (typeof value === "object") {
+ if (Array.isArray(value)) {
+ (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key));
+ } else if (value instanceof Date) {
+ if (key != null) {
+ httpParams = httpParams.append(key,
+ (value as Date).toISOString().substr(0, 10));
+ } else {
+ throw Error("key may not be null if value is Date");
+ }
+ } else {
+ Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive(
+ httpParams, value[k], key != null ? `${key}.${k}` : k));
+ }
+ } else if (key != null) {
+ httpParams = httpParams.append(key, value);
+ } else {
+ throw Error("key may not be null if value is not object or array");
+ }
+ return httpParams;
+ }
+
/**
* Login user to open-banking to perform payment (anonymous to OPBA)
* TBD
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
+ * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
* @param authorizationId Authorization session ID to approve
* @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'body', reportProgress?: boolean): Observable;
- public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'response', reportProgress?: boolean): Observable>;
- public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'events', reportProgress?: boolean): Observable>;
- public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe: any = 'body', reportProgress: boolean = false ): Observable {
+ public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling loginForAnonymousPaymentApproval.');
+ throw new Error('Required parameter xRequestID was null or undefined when calling loginForAnonymousApproval.');
}
if (authorizationId === null || authorizationId === undefined) {
- throw new Error('Required parameter authorizationId was null or undefined when calling loginForAnonymousPaymentApproval.');
+ throw new Error('Required parameter authorizationId was null or undefined when calling loginForAnonymousApproval.');
}
if (redirectCode === null || redirectCode === undefined) {
- throw new Error('Required parameter redirectCode was null or undefined when calling loginForAnonymousPaymentApproval.');
+ throw new Error('Required parameter redirectCode was null or undefined when calling loginForAnonymousApproval.');
}
let queryParameters = new HttpParams({encoder: this.encoder});
if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
+ queryParameters = this.addToHttpParams(queryParameters,
+ redirectCode, 'redirectCode');
}
let headers = this.defaultHeaders;
@@ -80,20 +121,29 @@ export class PsuAuthenticationAndConsentApprovalService {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (httpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
- return this.httpClient.post(`${this.configuration.basePath}/v1/psu/pis/${encodeURIComponent(String(authorizationId))}/anonymous`,
+ let responseType: 'text' | 'json' = 'json';
+ if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
+ responseType = 'text';
+ }
+
+ return this.httpClient.post(`${this.configuration.basePath}/v1/psu/${encodeURIComponent(String(authorizationId))}/for-approval/anonymous`,
null,
{
params: queryParameters,
+ responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
@@ -105,17 +155,17 @@ export class PsuAuthenticationAndConsentApprovalService {
/**
* Login user to open-banking
* TBD
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
+ * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
* @param authorizationId Authorization session ID to approve
* @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification
* @param psuAuthBody User credentials object
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable;
- public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>;
- public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>;
- public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable {
+ public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling loginForApproval.');
}
@@ -131,7 +181,8 @@ export class PsuAuthenticationAndConsentApprovalService {
let queryParameters = new HttpParams({encoder: this.encoder});
if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
+ queryParameters = this.addToHttpParams(queryParameters,
+ redirectCode, 'redirectCode');
}
let headers = this.defaultHeaders;
@@ -139,11 +190,14 @@ export class PsuAuthenticationAndConsentApprovalService {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (httpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
@@ -158,78 +212,16 @@ export class PsuAuthenticationAndConsentApprovalService {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
- return this.httpClient.post(`${this.configuration.basePath}/v1/psu/ais/${encodeURIComponent(String(authorizationId))}/for-approval/login`,
- psuAuthBody,
- {
- params: queryParameters,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Login user to open-banking to perform payment
- * TBD
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param authorizationId Authorization session ID to approve
- * @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification
- * @param psuAuthBody User credentials object
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable;
- public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>;
- public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>;
- public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling loginForPaymentApproval.');
- }
- if (authorizationId === null || authorizationId === undefined) {
- throw new Error('Required parameter authorizationId was null or undefined when calling loginForPaymentApproval.');
- }
- if (redirectCode === null || redirectCode === undefined) {
- throw new Error('Required parameter redirectCode was null or undefined when calling loginForPaymentApproval.');
- }
- if (psuAuthBody === null || psuAuthBody === undefined) {
- throw new Error('Required parameter psuAuthBody was null or undefined when calling loginForPaymentApproval.');
- }
-
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
- }
-
- let headers = this.defaultHeaders;
- if (xRequestID !== undefined && xRequestID !== null) {
- headers = headers.set('X-Request-ID', String(xRequestID));
- }
-
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
+ let responseType: 'text' | 'json' = 'json';
+ if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
+ responseType = 'text';
}
- return this.httpClient.post(`${this.configuration.basePath}/v1/psu/pis/${encodeURIComponent(String(authorizationId))}/for-approval/login`,
+ return this.httpClient.post(`${this.configuration.basePath}/v1/psu/${encodeURIComponent(String(authorizationId))}/for-approval/login`,
psuAuthBody,
{
params: queryParameters,
+ responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
diff --git a/consent-ui/src/app/api/api.headers.ts b/consent-ui/src/app/api/api.headers.ts
index 5454ab05e7..0d40d4085e 100644
--- a/consent-ui/src/app/api/api.headers.ts
+++ b/consent-ui/src/app/api/api.headers.ts
@@ -1,5 +1,6 @@
export enum ApiHeaders {
REDIRECT_CODE = 'Redirect-Code',
+ X_XSRF_TOKEN = 'X-XSRF-TOKEN',
LOCATION = 'Location',
COOKIE_TTL = 'Cookie-TTL'
}
diff --git a/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts b/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts
index a66b0929a1..611d87b531 100644
--- a/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts
+++ b/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts
@@ -88,24 +88,24 @@ export class AuthStateConsentAuthorizationService {
/**
* Redirect entry point for initiating a consent authorization process.
- * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the redirectCode to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>).
+ * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the xXsrfToken to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>).
* @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
+ * @param xXsrfToken XSRF parameter used to retrieve a redirect session. This is generaly transported as a query parameter.
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public authUsingGET(authId: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
+ public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public authUsingGET(authId: string, xXsrfToken?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (authId === null || authId === undefined) {
throw new Error('Required parameter authId was null or undefined when calling authUsingGET.');
}
let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
+ if (xXsrfToken !== undefined && xXsrfToken !== null) {
queryParameters = this.addToHttpParams(queryParameters,
- redirectCode, 'redirectCode');
+ xXsrfToken, 'xXsrfToken');
}
let headers = this.defaultHeaders;
diff --git a/consent-ui/src/app/api/api/consentAuthorization.service.ts b/consent-ui/src/app/api/api/consentAuthorization.service.ts
deleted file mode 100644
index 271238c543..0000000000
--- a/consent-ui/src/app/api/api/consentAuthorization.service.ts
+++ /dev/null
@@ -1,400 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-/* tslint:disable:no-unused-variable member-ordering */
-
-import { Inject, Injectable, Optional } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams,
- HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http';
-import { CustomHttpParameterCodec } from '../encoder';
-import { Observable } from 'rxjs';
-
-import { AuthorizeRequest } from '../model/authorizeRequest';
-import { DenyRequest } from '../model/denyRequest';
-import { InlineResponse200 } from '../model/inlineResponse200';
-import { PsuAuthRequest } from '../model/psuAuthRequest';
-import { PsuMessage } from '../model/psuMessage';
-
-import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
-import { Configuration } from '../configuration';
-
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class ConsentAuthorizationService {
-
- protected basePath = 'http://localhost';
- public defaultHeaders = new HttpHeaders();
- public configuration = new Configuration();
- public encoder: HttpParameterCodec;
-
- constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) {
- if (configuration) {
- this.configuration = configuration;
- }
- if (typeof this.configuration.basePath !== 'string') {
- if (typeof basePath !== 'string') {
- basePath = this.basePath;
- }
- this.configuration.basePath = basePath;
- }
- this.encoder = this.configuration.encoder || new CustomHttpParameterCodec();
- }
-
-
-
- /**
- * Redirect entry point for initiating a consent authorization process.
- * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the redirectCode to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>).
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable;
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>;
- public authUsingGET(authId: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>;
- public authUsingGET(authId: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling authUsingGET.');
- }
-
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
- }
-
- let headers = this.defaultHeaders;
-
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}`,
- {
- params: queryParameters,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Consent authorization is denied - consent is blocked. Closes this session and redirects the PSU back to the FinTechApi or close the application window.
- * Closes this session and redirects the PSU back to the FinTechApi or close the application window. In any case, the session of the user will be closed and cookies will be deleted with the response to this request.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param denyRequest
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'body', reportProgress?: boolean): Observable;
- public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'response', reportProgress?: boolean): Observable>;
- public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'events', reportProgress?: boolean): Observable>;
- public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling denyUsingPOST.');
- }
- if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling denyUsingPOST.');
- }
- if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) {
- throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling denyUsingPOST.');
- }
- if (denyRequest === null || denyRequest === undefined) {
- throw new Error('Required parameter denyRequest was null or undefined when calling denyUsingPOST.');
- }
-
- let headers = this.defaultHeaders;
- if (xRequestID !== undefined && xRequestID !== null) {
- headers = headers.set('X-Request-ID', String(xRequestID));
- }
- if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN));
- }
-
- // authentication (sessionCookie) required
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
- }
-
- return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/deny`,
- denyRequest,
- {
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Generic challenge response end point for updating consent session with PSU authentication data while requesting remaining challenges for the ongoing authorization process.
- * Update consent session with PSU auth data whereby requesting remaining challenges for the ongoing authorization process. Returns 202 if one should proceed to some other link. Link to follow is in \'Location\' header.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
- * @param psuAuthRequest
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean): Observable;
- public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean): Observable>;
- public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean): Observable>;
- public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling embeddedUsingPOST.');
- }
- if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling embeddedUsingPOST.');
- }
- if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) {
- throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling embeddedUsingPOST.');
- }
-
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
- }
-
- let headers = this.defaultHeaders;
- if (xRequestID !== undefined && xRequestID !== null) {
- headers = headers.set('X-Request-ID', String(xRequestID));
- }
- if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN));
- }
-
- // authentication (sessionCookie) required
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
- }
-
- return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/embedded`,
- psuAuthRequest,
- {
- params: queryParameters,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Redirecting back from ASPSP to TPP after a failed consent authorization.
- * Redirecting back from ASPSP to TPP after a failed consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling fromAspspNokUsingGET.');
- }
- if (redirectState === null || redirectState === undefined) {
- throw new Error('Required parameter redirectState was null or undefined when calling fromAspspNokUsingGET.');
- }
-
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
- }
-
- let headers = this.defaultHeaders;
-
- // authentication (redirectCookie) required
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/nok`,
- {
- params: queryParameters,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization.
- * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting redirectCode will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this redirectCode to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the redirectCode and proceed forward with the authorization process.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling fromAspspOkUsingGET.');
- }
- if (redirectState === null || redirectState === undefined) {
- throw new Error('Required parameter redirectState was null or undefined when calling fromAspspOkUsingGET.');
- }
-
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = queryParameters.set('redirectCode', redirectCode);
- }
-
- let headers = this.defaultHeaders;
-
- // authentication (redirectCookie) required
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/ok`,
- {
- params: queryParameters,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
- /**
- * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP.
- * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param authorizeRequest
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'body', reportProgress?: boolean): Observable;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'response', reportProgress?: boolean): Observable>;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'events', reportProgress?: boolean): Observable>;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe: any = 'body', reportProgress: boolean = false ): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) {
- throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (authorizeRequest === null || authorizeRequest === undefined) {
- throw new Error('Required parameter authorizeRequest was null or undefined when calling toAspspGrantUsingPOST.');
- }
-
- let headers = this.defaultHeaders;
- if (xRequestID !== undefined && xRequestID !== null) {
- headers = headers.set('X-Request-ID', String(xRequestID));
- }
- if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN));
- }
-
- // authentication (sessionCookie) required
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
- }
-
- return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/toAspsp/grant`,
- authorizeRequest,
- {
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
-}
diff --git a/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts b/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts
index b8eb908f94..c9c5aae285 100644
--- a/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts
+++ b/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts
@@ -90,15 +90,14 @@ export class FromASPSPConsentAuthorizationService {
* Redirecting back from ASPSP to TPP after a failed consent authorization.
* Redirecting back from ASPSP to TPP after a failed consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request.
* @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
+ * @param redirectState Code used to retrieve a redirect session. This is generaly transported as a path parameter due to some banks limitiations (ING ASPSP) instead of being transported as query parameter
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
+ public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public fromAspspNokUsingGET(authId: string, redirectState: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (authId === null || authId === undefined) {
throw new Error('Required parameter authId was null or undefined when calling fromAspspNokUsingGET.');
}
@@ -106,12 +105,6 @@ export class FromASPSPConsentAuthorizationService {
throw new Error('Required parameter redirectState was null or undefined when calling fromAspspNokUsingGET.');
}
- let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = this.addToHttpParams(queryParameters,
- redirectCode, 'redirectCode');
- }
-
let headers = this.defaultHeaders;
// authentication (redirectCookie) required
@@ -141,7 +134,6 @@ export class FromASPSPConsentAuthorizationService {
return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/nok`,
{
- params: queryParameters,
responseType: responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
@@ -153,18 +145,17 @@ export class FromASPSPConsentAuthorizationService {
/**
* Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization.
- * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting redirectCode will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this redirectCode to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the redirectCode and proceed forward with the authorization process.
+ * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting xXsrfToken will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this xXsrfToken to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the xXsrfToken and proceed forward with the authorization process.
* @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
+ * @param redirectState Code used to retrieve a redirect session. This is generaly transported as a path parameter due to some banks limitiations (ING ASPSP) instead of being transported as query parameter
* @param code Oauth2 code to exchange for token.
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
+ public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (authId === null || authId === undefined) {
throw new Error('Required parameter authId was null or undefined when calling fromAspspOkUsingGET.');
}
@@ -173,10 +164,6 @@ export class FromASPSPConsentAuthorizationService {
}
let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
- queryParameters = this.addToHttpParams(queryParameters,
- redirectCode, 'redirectCode');
- }
if (code !== undefined && code !== null) {
queryParameters = this.addToHttpParams(queryParameters,
code, 'code');
diff --git a/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts b/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts
index 001c047c76..0722bbb161 100644
--- a/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts
+++ b/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts
@@ -17,9 +17,7 @@ import { HttpClient, HttpHeaders, HttpParams,
import { CustomHttpParameterCodec } from '../encoder';
import { Observable } from 'rxjs';
-import { AuthorizeRequest } from '../model/models';
import { ConsentAuth } from '../model/models';
-import { DenyRequest } from '../model/models';
import { PsuAuthRequest } from '../model/models';
import { PsuMessage } from '../model/models';
@@ -94,35 +92,24 @@ export class UpdateConsentAuthorizationService {
* Closes this session and redirects the PSU back to the FinTechApi or close the application window. In any case, the session of the user will be closed and cookies will be deleted with the response to this request.
* @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
* @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param denyRequest
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
+ public denyUsingPOST(authId: string, xRequestID: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public denyUsingPOST(authId: string, xRequestID: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public denyUsingPOST(authId: string, xRequestID: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public denyUsingPOST(authId: string, xRequestID: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (authId === null || authId === undefined) {
throw new Error('Required parameter authId was null or undefined when calling denyUsingPOST.');
}
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling denyUsingPOST.');
}
- if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) {
- throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling denyUsingPOST.');
- }
- if (denyRequest === null || denyRequest === undefined) {
- throw new Error('Required parameter denyRequest was null or undefined when calling denyUsingPOST.');
- }
let headers = this.defaultHeaders;
if (xRequestID !== undefined && xRequestID !== null) {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN));
- }
// authentication (sessionCookie) required
if (this.configuration.apiKeys) {
@@ -144,22 +131,13 @@ export class UpdateConsentAuthorizationService {
}
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
- }
-
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/deny`,
- denyRequest,
+ null,
{
responseType: responseType,
withCredentials: this.configuration.withCredentials,
@@ -175,39 +153,32 @@ export class UpdateConsentAuthorizationService {
* Update consent session with PSU auth data whereby requesting remaining challenges for the ongoing authorization process. Returns 202 if one should proceed to some other link. Link to follow is in \'Location\' header.
* @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
* @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter
+ * @param xXsrfToken XSRF parameter used to retrieve a redirect session. This is generaly transported as a query parameter.
* @param psuAuthRequest
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
+ public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
+ public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
+ public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
if (authId === null || authId === undefined) {
throw new Error('Required parameter authId was null or undefined when calling embeddedUsingPOST.');
}
if (xRequestID === null || xRequestID === undefined) {
throw new Error('Required parameter xRequestID was null or undefined when calling embeddedUsingPOST.');
}
- if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) {
- throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling embeddedUsingPOST.');
- }
let queryParameters = new HttpParams({encoder: this.encoder});
- if (redirectCode !== undefined && redirectCode !== null) {
+ if (xXsrfToken !== undefined && xXsrfToken !== null) {
queryParameters = this.addToHttpParams(queryParameters,
- redirectCode, 'redirectCode');
+ xXsrfToken, 'xXsrfToken');
}
let headers = this.defaultHeaders;
if (xRequestID !== undefined && xRequestID !== null) {
headers = headers.set('X-Request-ID', String(xRequestID));
}
- if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN));
- }
// authentication (sessionCookie) required
if (this.configuration.apiKeys) {
@@ -256,85 +227,4 @@ export class UpdateConsentAuthorizationService {
);
}
- /**
- * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP.
- * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP.
- * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process.
- * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well.
- * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie.
- * @param authorizeRequest
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>;
- public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable {
- if (authId === null || authId === undefined) {
- throw new Error('Required parameter authId was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (xRequestID === null || xRequestID === undefined) {
- throw new Error('Required parameter xRequestID was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) {
- throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling toAspspGrantUsingPOST.');
- }
- if (authorizeRequest === null || authorizeRequest === undefined) {
- throw new Error('Required parameter authorizeRequest was null or undefined when calling toAspspGrantUsingPOST.');
- }
-
- let headers = this.defaultHeaders;
- if (xRequestID !== undefined && xRequestID !== null) {
- headers = headers.set('X-Request-ID', String(xRequestID));
- }
- if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) {
- headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN));
- }
-
- // authentication (sessionCookie) required
- if (this.configuration.apiKeys) {
- const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"];
- if (key) {
- }
- }
-
- let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
- if (httpHeaderAcceptSelected === undefined) {
- // to determine the Accept header
- const httpHeaderAccepts: string[] = [
- 'application/json'
- ];
- httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
- }
- if (httpHeaderAcceptSelected !== undefined) {
- headers = headers.set('Accept', httpHeaderAcceptSelected);
- }
-
-
- // to determine the Content-Type header
- const consumes: string[] = [
- 'application/json'
- ];
- const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
- if (httpContentTypeSelected !== undefined) {
- headers = headers.set('Content-Type', httpContentTypeSelected);
- }
-
- let responseType: 'text' | 'json' = 'json';
- if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
- responseType = 'text';
- }
-
- return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/toAspsp/grant`,
- authorizeRequest,
- {
- responseType: responseType,
- withCredentials: this.configuration.withCredentials,
- headers: headers,
- observe: observe,
- reportProgress: reportProgress
- }
- );
- }
-
}
diff --git a/consent-ui/src/app/api/model/authorizeRequest.ts b/consent-ui/src/app/api/model/authorizeRequest.ts
deleted file mode 100644
index cea4b4d948..0000000000
--- a/consent-ui/src/app/api/model/authorizeRequest.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ConsentAuthRequirement } from './consentAuthRequirement';
-
-
-/**
- * Contains information used to legitimate a request.
- */
-export interface AuthorizeRequest {
- consentAuth?: ConsentAuthRequirement;
-}
-
diff --git a/consent-ui/src/app/api/model/consentAuth.ts b/consent-ui/src/app/api/model/consentAuth.ts
index 0a229eaf82..a7a2f18f1f 100644
--- a/consent-ui/src/app/api/model/consentAuth.ts
+++ b/consent-ui/src/app/api/model/consentAuth.ts
@@ -27,6 +27,7 @@ export interface ConsentAuth {
action?: ConsentAuth.ActionEnum;
violations?: Array;
accounts?: Array;
+ supportedConsentTypes?: Array;
authMessageTemplate?: string;
/**
* An identification provided by the ASPSP for the later identification of the authentication method selection.
@@ -53,6 +54,7 @@ export interface ConsentAuth {
* List of sca methods for selection if necessary.
*/
scaMethods?: Array;
+ scaMethodSelected?: ScaUserData;
scaStatus?: ScaStatus;
singlePayment?: SinglePayment;
challengeData?: ChallengeData;
@@ -64,6 +66,12 @@ export namespace ConsentAuth {
LISTTRANSACTIONS: 'LIST_TRANSACTIONS' as ActionEnum,
INITIATEPAYMENT: 'INITIATE_PAYMENT' as ActionEnum
};
+ export type SupportedConsentTypesEnum = 'DEDICATED_ALL' | 'GLOBAL_ALL' | 'GLOBAL_ACCOUNTS';
+ export const SupportedConsentTypesEnum = {
+ DEDICATEDALL: 'DEDICATED_ALL' as SupportedConsentTypesEnum,
+ GLOBALALL: 'GLOBAL_ALL' as SupportedConsentTypesEnum,
+ GLOBALACCOUNTS: 'GLOBAL_ACCOUNTS' as SupportedConsentTypesEnum
+ };
}
diff --git a/consent-ui/src/app/api/model/consentAuthRequiredField.ts b/consent-ui/src/app/api/model/consentAuthRequiredField.ts
deleted file mode 100644
index 27f1a9fa59..0000000000
--- a/consent-ui/src/app/api/model/consentAuthRequiredField.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-
-
-/**
- * Fields that are required to be filled in order to authorize consent
- */
-export interface ConsentAuthRequiredField {
- /**
- * Field data type - boolean, string, etc.
- */
- type?: string;
- /**
- * Scope of the field.
- */
- scope?: string;
- /**
- * Field code - what does the field mean.
- */
- code?: string;
- /**
- * Custom message that describes field meaning
- */
- captionMessage?: string;
-}
-
diff --git a/consent-ui/src/app/api/model/consentAuthRequirement.ts b/consent-ui/src/app/api/model/consentAuthRequirement.ts
deleted file mode 100644
index f65b422233..0000000000
--- a/consent-ui/src/app/api/model/consentAuthRequirement.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ConsentAuthRequiredField } from './consentAuthRequiredField';
-
-
-/**
- * Transport object for consent API request response
- */
-export interface ConsentAuthRequirement {
- fields?: Array;
-}
-
diff --git a/consent-ui/src/app/api/model/denyRedirectRequest.ts b/consent-ui/src/app/api/model/denyRedirectRequest.ts
deleted file mode 100644
index 160228642c..0000000000
--- a/consent-ui/src/app/api/model/denyRedirectRequest.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ConsentAuth } from './consentAuth';
-
-
-/**
- * Denies a redirect to ASPSP requested by the ConsentAuthorisationApi
- */
-export interface DenyRedirectRequest {
- consentAuth?: ConsentAuth;
- /**
- * Will indicate if PSU wants to be sent back to FinTechApi.
- */
- backToFinTech?: boolean;
- /**
- * In case there is no redirect back to TPP desired, exit page can be specified by ConsentAuthorisationApi
- */
- exitPage?: string;
- /**
- * Set to true if consent object shall be forgotten or frozen.
- */
- forgetConsent?: boolean;
-}
-
diff --git a/consent-ui/src/app/api/model/denyRequest.ts b/consent-ui/src/app/api/model/denyRequest.ts
deleted file mode 100644
index 703a40a0a2..0000000000
--- a/consent-ui/src/app/api/model/denyRequest.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ConsentAuth } from './consentAuth';
-
-
-/**
- * Consent authorization denied descriptor
- */
-export interface DenyRequest {
- consentAuth?: ConsentAuth;
- /**
- * Will indicate if PSU wants to be sent back to FinTechApi.
- */
- backToFinTech?: boolean;
- /**
- * In case there is no redirect back to TPP desired, exit page can be specified by ConsentAuthorisationApi
- */
- exitPage?: string;
- /**
- * Set to true if consent object shall be forgotten or frozen.
- */
- forgetConsent?: boolean;
-}
-
diff --git a/consent-ui/src/app/api/model/inlineResponse200.ts b/consent-ui/src/app/api/model/inlineResponse200.ts
deleted file mode 100644
index 1a326dcbea..0000000000
--- a/consent-ui/src/app/api/model/inlineResponse200.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Open Banking Gateway - Consent Authorization API.
- * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU.
- *
- * The version of the OpenAPI document: 1.0
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ConsentAuth } from './consentAuth';
-
-
-export interface InlineResponse200 {
- consentAuth?: ConsentAuth;
-}
-
diff --git a/consent-ui/src/app/api/model/models.ts b/consent-ui/src/app/api/model/models.ts
index 6059635bb8..0ae600ff23 100644
--- a/consent-ui/src/app/api/model/models.ts
+++ b/consent-ui/src/app/api/model/models.ts
@@ -8,14 +8,10 @@ export * from './aisAccountAccessInfo';
export * from './aisConsentRequest';
export * from './amount';
export * from './authViolation';
-export * from './authorizeRequest';
export * from './balanceType';
export * from './bulkPayment';
export * from './challengeData';
export * from './consentAuth';
-export * from './consentAuthRequiredField';
-export * from './consentAuthRequirement';
-export * from './denyRequest';
export * from './paymentProduct';
export * from './paymentStatus';
export * from './periodicPayment';
diff --git a/consent-ui/src/app/api/model/scaUserData.ts b/consent-ui/src/app/api/model/scaUserData.ts
index 267f2711f8..5e8518a7e5 100644
--- a/consent-ui/src/app/api/model/scaUserData.ts
+++ b/consent-ui/src/app/api/model/scaUserData.ts
@@ -15,6 +15,7 @@ export interface ScaUserData {
decoupled?: boolean;
id?: string;
methodValue?: string;
+ explanation?: string;
scaMethod?: ScaUserData.ScaMethodEnum;
staticTan?: string;
usesStaticTan?: boolean;
diff --git a/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts b/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts
index d070f3f46e..9c13563fb3 100644
--- a/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts
+++ b/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts
@@ -45,7 +45,7 @@ describe('AnonymousComponent', () => {
});
it('should call login service', () => {
- authServiceSpy = spyOn(authService, 'userLoginForAnonymousPayment').and.callThrough();
+ authServiceSpy = spyOn(authService, 'userLoginForAnonymous').and.callThrough();
const authID = route.snapshot.parent.params.authId;
const redirectCode = 'redirectCode654';
diff --git a/consent-ui/src/app/auth/anonymous/anonymous.component.ts b/consent-ui/src/app/auth/anonymous/anonymous.component.ts
index 4d48d2e273..5a69ad81ca 100644
--- a/consent-ui/src/app/auth/anonymous/anonymous.component.ts
+++ b/consent-ui/src/app/auth/anonymous/anonymous.component.ts
@@ -38,7 +38,7 @@ export class AnonymousComponent implements OnInit {
private doLoginAsAnonymous() {
localStorage.setItem(ApiHeaders.COOKIE_TTL, '0');
- this.authService.userLoginForAnonymousPayment(this.authId, this.redirectCode).subscribe((res) => {
+ this.authService.userLoginForAnonymous(this.authId, this.redirectCode).subscribe((res) => {
this.sessionService.setTTL(this.authId, res.headers.get(ApiHeaders.COOKIE_TTL));
window.location.href = res.headers.get(ApiHeaders.LOCATION);
});
diff --git a/consent-ui/src/app/auth/login/login.component.spec.ts b/consent-ui/src/app/auth/login/login.component.spec.ts
index 880867d4e9..d5ede135e7 100644
--- a/consent-ui/src/app/auth/login/login.component.spec.ts
+++ b/consent-ui/src/app/auth/login/login.component.spec.ts
@@ -59,7 +59,7 @@ describe('LoginComponent', () => {
});
it('should be true if the form is invalid', () => {
- authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response));
+ authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response));
form.controls.login.setValue(usernameInput);
form.controls.password.setValue('');
@@ -70,7 +70,7 @@ describe('LoginComponent', () => {
});
it('should call login service', () => {
- authServiceSpy = spyOn(authService, 'userLoginForConsent').and.callThrough();
+ authServiceSpy = spyOn(authService, 'userLogin').and.callThrough();
const authID = route.snapshot.parent.params.authId;
const redirectCode = 'redirectCode654';
@@ -86,7 +86,7 @@ describe('LoginComponent', () => {
});
it('should be invalid if password is not set', () => {
- authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response));
+ authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response));
form.controls.login.setValue(usernameInput);
form.controls.password.setValue('');
@@ -96,7 +96,7 @@ describe('LoginComponent', () => {
expect(component.loginForm.invalid).toBe(true);
});
it('should be invalid if username is not set', () => {
- authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response));
+ authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response));
form.controls.login.setValue('');
form.controls.password.setValue(passwordInput);
diff --git a/consent-ui/src/app/auth/login/login.component.ts b/consent-ui/src/app/auth/login/login.component.ts
index 95dc908b9b..d49e75a1df 100644
--- a/consent-ui/src/app/auth/login/login.component.ts
+++ b/consent-ui/src/app/auth/login/login.component.ts
@@ -47,7 +47,7 @@ export class LoginComponent implements OnInit {
onSubmit() {
localStorage.setItem(ApiHeaders.COOKIE_TTL, '0');
- this.authService.userLoginForConsent(this.authId, this.redirectCode, this.loginForm.value).subscribe((res) => {
+ this.authService.userLogin(this.authId, this.redirectCode, this.loginForm.value).subscribe((res) => {
this.sessionService.setTTL(this.authId, res.headers.get(ApiHeaders.COOKIE_TTL));
window.location.href = res.headers.get(ApiHeaders.LOCATION);
});
diff --git a/consent-ui/src/app/common/auth.service.ts b/consent-ui/src/app/common/auth.service.ts
index f05dcef9f2..3c3f4faaa3 100644
--- a/consent-ui/src/app/common/auth.service.ts
+++ b/consent-ui/src/app/common/auth.service.ts
@@ -11,13 +11,13 @@ export class AuthService {
constructor(
private http: HttpClient,
private psuAuthService: PsuAuthenticationService,
- private psuAuthForConsentApproval: PsuAuthenticationAndConsentApprovalService,
+ private psuAuth: PsuAuthenticationAndConsentApprovalService,
private sessionService: SessionService
) {}
- public userLoginForConsent(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) {
+ public userLogin(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) {
const xRequestID = uuid.v4();
- return this.psuAuthForConsentApproval.loginForApproval(
+ return this.psuAuth.loginForApproval(
xRequestID,
authorizationId,
redirectCode,
@@ -26,9 +26,9 @@ export class AuthService {
);
}
- public userLoginForAnonymousPayment(authorizationId: string, redirectCode: string) {
+ public userLoginForAnonymous(authorizationId: string, redirectCode: string) {
const xRequestID = uuid.v4();
- return this.psuAuthForConsentApproval.loginForAnonymousPaymentApproval(
+ return this.psuAuth.loginForAnonymousApproval(
xRequestID,
authorizationId,
redirectCode,
@@ -36,21 +36,6 @@ export class AuthService {
);
}
- public userLoginForPayment(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) {
- const xRequestID = uuid.v4();
- return this.psuAuthForConsentApproval.loginForPaymentApproval(
- xRequestID,
- authorizationId,
- redirectCode,
- credentials,
- 'response'
- );
- }
-
- public userLogin(credentials: PsuAuthBody) {
- const xRequestID = uuid.v4();
- return this.psuAuthService.login(xRequestID, credentials, 'response');
- }
public userRegister(credentials: PsuAuthBody) {
const xRequestID = uuid.v4();
return this.psuAuthService.registration(xRequestID, credentials, 'response');
diff --git a/consent-ui/src/app/common/enter-pin/enter-pin.component.ts b/consent-ui/src/app/common/enter-pin/enter-pin.component.ts
index a05bbc2e23..2549fa8081 100644
--- a/consent-ui/src/app/common/enter-pin/enter-pin.component.ts
+++ b/consent-ui/src/app/common/enter-pin/enter-pin.component.ts
@@ -38,13 +38,12 @@ export class EnterPinComponent implements OnInit {
.embeddedUsingPOST(
this.authorizationSessionId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
this.redirectCode,
{ scaAuthenticationData: { PSU_PASSWORD: this.pinForm.get('pin').value } },
'response'
)
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.enteredPin.emit(res);
});
}
diff --git a/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts b/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts
index 88e699822d..351ae6d6fb 100644
--- a/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts
+++ b/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts
@@ -8,7 +8,7 @@ import { EnterTanComponent } from './enter-tan.component';
import { StubUtilTests } from '../../ais/common/stub-util-tests';
import { SessionService } from '../session.service';
import { UpdateConsentAuthorizationService } from '../../api';
-import { ConsentAuthorizationService } from '../../api/api/consentAuthorization.service';
+import { AuthStateConsentAuthorizationService } from '../../api';
describe('EnterTanComponent', () => {
let component: EnterTanComponent;
@@ -34,7 +34,7 @@ describe('EnterTanComponent', () => {
component = fixture.componentInstance;
sessionService = TestBed.inject(SessionService);
updateConsentAuthorizationService = TestBed.inject(UpdateConsentAuthorizationService);
- consentAuthorizationService = TestBed.inject(ConsentAuthorizationService);
+ consentAuthorizationService = TestBed.inject(AuthStateConsentAuthorizationService);
sessionServiceSpy = spyOn(sessionService, 'getRedirectCode').and.returnValue(StubUtilTests.REDIRECT_ID);
updateConsentAuthorizationServiceSpy = spyOn(
updateConsentAuthorizationService,
diff --git a/consent-ui/src/app/common/enter-tan/enter-tan.component.ts b/consent-ui/src/app/common/enter-tan/enter-tan.component.ts
index 674779859f..fee6a2a9c2 100644
--- a/consent-ui/src/app/common/enter-tan/enter-tan.component.ts
+++ b/consent-ui/src/app/common/enter-tan/enter-tan.component.ts
@@ -5,7 +5,7 @@ import { StubUtil } from '../utils/stub-util';
import { UpdateConsentAuthorizationService } from '../../api';
import { ApiHeaders } from '../../api/api.headers';
import { SessionService } from '../session.service';
-import { ConsentAuthorizationService } from '../../api/api/consentAuthorization.service';
+import { AuthStateConsentAuthorizationService } from '../../api/api/authStateConsentAuthorization.service';
@Component({
selector: 'consent-app-enter-tan',
@@ -30,7 +30,7 @@ export class EnterTanComponent implements OnInit {
constructor(
private formBuilder: FormBuilder,
private sessionService: SessionService,
- private consentAuthorizationService: ConsentAuthorizationService,
+ private consentAuthorizationService: AuthStateConsentAuthorizationService,
private updateConsentAuthorizationService: UpdateConsentAuthorizationService
) {}
@@ -46,7 +46,7 @@ export class EnterTanComponent implements OnInit {
.subscribe((response) => {
this.sessionService.setRedirectCode(
this.authorizationSessionId,
- response.headers.get(ApiHeaders.REDIRECT_CODE)
+ response.headers.get(ApiHeaders.X_XSRF_TOKEN)
);
const authStateResponseBody: any = response.body;
@@ -70,14 +70,13 @@ export class EnterTanComponent implements OnInit {
this.updateConsentAuthorizationService
.embeddedUsingPOST(
this.authorizationSessionId,
- StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
+ StubUtil.X_REQUEST_ID, // TODO: real values instead of stub
this.sessionService.getRedirectCode(this.authorizationSessionId),
{ scaAuthenticationData: { SCA_CHALLENGE_DATA: this.reportScaResultForm.get('tan').value } },
'response'
)
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.enteredSca.emit(res);
});
}
diff --git a/consent-ui/src/app/common/select-sca/select-sca.component.ts b/consent-ui/src/app/common/select-sca/select-sca.component.ts
index 5a89c94a50..57c30835a4 100644
--- a/consent-ui/src/app/common/select-sca/select-sca.component.ts
+++ b/consent-ui/src/app/common/select-sca/select-sca.component.ts
@@ -40,14 +40,13 @@ export class SelectScaComponent implements OnInit {
this.updateConsentAuthorizationService
.embeddedUsingPOST(
this.authorizationSessionId,
- StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
+ StubUtil.X_REQUEST_ID, // TODO: real values instead of stub
this.redirectCode,
{ scaAuthenticationData: { SCA_CHALLENGE_ID: this.selectedMethod.value } },
'response'
)
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.selectedValue.emit(res);
});
}
@@ -58,7 +57,7 @@ export class SelectScaComponent implements OnInit {
.subscribe((consentAuth) => {
this.sessionService.setRedirectCode(
this.authorizationSessionId,
- consentAuth.headers.get(ApiHeaders.REDIRECT_CODE)
+ consentAuth.headers.get(ApiHeaders.X_XSRF_TOKEN)
);
this.redirectCode = this.sessionService.getRedirectCode(this.authorizationSessionId);
this.scaMethods = consentAuth.body.scaMethods;
diff --git a/consent-ui/src/app/common/session.service.ts b/consent-ui/src/app/common/session.service.ts
index 1914d583bb..ba223f8c1e 100644
--- a/consent-ui/src/app/common/session.service.ts
+++ b/consent-ui/src/app/common/session.service.ts
@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
+import {ConsentAuth} from '../api';
@Injectable({
providedIn: 'root'
@@ -37,6 +38,18 @@ export class SessionService {
sessionStorage.setItem(authorizationId + Session.FINTECH_NAME, fintechName);
}
+ public getConsentTypesSupported(authorizationId: string): ConsentAuth.SupportedConsentTypesEnum[] {
+ const supportedTypes = sessionStorage.getItem(authorizationId + Session.CONSENT_TYPES_SUPPORTED);
+ if (!supportedTypes) {
+ return null;
+ }
+ return JSON.parse(supportedTypes);
+ }
+
+ public setConsentTypesSupported(authorizationId: string, consentTypes: ConsentAuth.SupportedConsentTypesEnum[]) {
+ sessionStorage.setItem(authorizationId + Session.CONSENT_TYPES_SUPPORTED, !consentTypes ? null : JSON.stringify(consentTypes));
+ }
+
public getBankName(authorizationId: string): string {
return sessionStorage.getItem(authorizationId + Session.BANK_NAME);
}
@@ -108,6 +121,7 @@ enum Session {
PAYMENT_OBJECT = ':PAYMENT_OBJECT',
PAYMENT_STATE = ':PAYMENT_STATE',
FINTECH_NAME = ':FINTECH_NAME',
+ CONSENT_TYPES_SUPPORTED = ':CONSENT_TYPES_SUPPORTED',
BANK_NAME = ':BANK_NAME',
XSRF_TOKEN = 'XSRF_TOKEN',
COOKIE_TTL = 'Cookie-TTL',
diff --git a/consent-ui/src/app/common/utils/stub-util.ts b/consent-ui/src/app/common/utils/stub-util.ts
index 6be08df249..821d86c56f 100644
--- a/consent-ui/src/app/common/utils/stub-util.ts
+++ b/consent-ui/src/app/common/utils/stub-util.ts
@@ -2,6 +2,5 @@
export class StubUtil {
public static FINTECH_NAME = 'Awesome FinTech';
public static ASPSP_NAME = 'adorsys Sandbox';
- public static X_XSRF_TOKEN = '12b34483-242a-428b-8295-2f4805bb0a30';
public static X_REQUEST_ID = 'a8b24683-242a-428b-8295-2f4805bb0a30';
}
diff --git a/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts b/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts
index ed464fd4c7..1092904e67 100644
--- a/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts
+++ b/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts
@@ -1,14 +1,14 @@
-import { AfterContentChecked, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
-
-import { DenyRequest, UpdateConsentAuthorizationService } from '../../api';
-import { ApiHeaders } from '../../api/api.headers';
-import { SessionService } from '../../common/session.service';
-import { AuthConsentState } from '../../ais/common/dto/auth-state';
-import { StubUtil } from '../../common/utils/stub-util';
-import { PaymentUtil } from '../common/payment-util';
-import { PisPayment } from '../common/models/pis-payment.model';
+import {AfterContentChecked, ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup} from '@angular/forms';
+import {ActivatedRoute, Router} from '@angular/router';
+
+import {UpdateConsentAuthorizationService} from '../../api';
+import {ApiHeaders} from '../../api/api.headers';
+import {SessionService} from '../../common/session.service';
+import {AuthConsentState} from '../../ais/common/dto/auth-state';
+import {StubUtil} from '../../common/utils/stub-util';
+import {PaymentUtil} from '../common/payment-util';
+import {PisPayment} from '../common/models/pis-payment.model';
@Component({
selector: 'consent-app-payment-access-selection',
@@ -70,8 +70,6 @@ export class ConsentPaymentAccessSelectionComponent implements OnInit, AfterCont
.denyUsingPOST(
this.authorizationId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
- {} as DenyRequest,
'response'
)
.subscribe((res) => {
diff --git a/consent-ui/src/app/pis/initiation/payment-initiate.component.ts b/consent-ui/src/app/pis/initiation/payment-initiate.component.ts
index 1e6855aaa3..70c45780e4 100644
--- a/consent-ui/src/app/pis/initiation/payment-initiate.component.ts
+++ b/consent-ui/src/app/pis/initiation/payment-initiate.component.ts
@@ -49,7 +49,7 @@ export class PaymentInitiateComponent implements OnInit {
this.authStateConsentAuthorizationService
.authUsingGET(authorizationId, redirectCode, 'response')
.subscribe((res) => {
- this.sessionService.setRedirectCode(authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
// setting bank and fintech names
this.sessionService.setBankName(authorizationId, (res.body as ConsentAuth).bankName);
diff --git a/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts b/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts
index e6d95099d2..198b1baef9 100644
--- a/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts
+++ b/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts
@@ -56,14 +56,13 @@ export class PaymentsConsentReviewComponent implements OnInit {
this.updateConsentAuthorizationService
.embeddedUsingPOST(
this.authorizationId,
- StubUtil.X_XSRF_TOKEN,
StubUtil.X_REQUEST_ID,
this.sessionService.getRedirectCode(this.authorizationId),
body,
'response'
)
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
window.location.href = res.headers.get(ApiHeaders.LOCATION);
});
}
diff --git a/consent-ui/src/app/pis/pis-routing.module.ts b/consent-ui/src/app/pis/pis-routing.module.ts
index d958ebe0b6..4370c3c9d2 100644
--- a/consent-ui/src/app/pis/pis-routing.module.ts
+++ b/consent-ui/src/app/pis/pis-routing.module.ts
@@ -9,6 +9,7 @@ import { ToAspspPageComponent } from './to-aspsp-page/to-aspsp-page.component';
import { ResultPageComponent } from './result-page/result-page.component';
import { EntryPagePaymentsComponent } from './entry-page-payments/entry-page-payments.component';
import { PaymentsConsentReviewComponent } from './payments-consent-review/payments-consent-review.component';
+import { WaitForDecoupled } from './wait-for-decoupled/wait-for-decoupled';
const routes: Routes = [
{
@@ -19,6 +20,7 @@ const routes: Routes = [
{ path: EnterTanPageComponent.ROUTE, component: EnterTanPageComponent },
{ path: SelectScaPageComponent.ROUTE, component: SelectScaPageComponent },
{ path: ToAspspPageComponent.ROUTE, component: ToAspspPageComponent },
+ { path: WaitForDecoupled.ROUTE, component: WaitForDecoupled },
{ path: ResultPageComponent.ROUTE, component: ResultPageComponent },
{
path: EntryPagePaymentsComponent.ROUTE,
diff --git a/consent-ui/src/app/pis/pis.module.ts b/consent-ui/src/app/pis/pis.module.ts
index fd100c78e3..c91117180c 100644
--- a/consent-ui/src/app/pis/pis.module.ts
+++ b/consent-ui/src/app/pis/pis.module.ts
@@ -13,6 +13,7 @@ import { PaymentsConsentReviewComponent } from './payments-consent-review/paymen
import { ConsentPaymentAccessSelectionComponent } from './consent-payment-access-selection/consent-payment-access-selection.component';
import { DynamicInputsComponent } from './dynamic-inputs/dynamic-inputs.component';
import { PaymentInfoComponent } from './payment-info/payment-info.component';
+import {WaitForDecoupled} from "./wait-for-decoupled/wait-for-decoupled";
@NgModule({
declarations: [
@@ -27,7 +28,8 @@ import { PaymentInfoComponent } from './payment-info/payment-info.component';
PaymentsConsentReviewComponent,
ConsentPaymentAccessSelectionComponent,
DynamicInputsComponent,
- PaymentInfoComponent
+ PaymentInfoComponent,
+ WaitForDecoupled
],
imports: [SharedModule, PisRoutingModule]
})
diff --git a/consent-ui/src/app/pis/result-page/result-page.component.ts b/consent-ui/src/app/pis/result-page/result-page.component.ts
index d5e1aade08..3ac041bf5f 100644
--- a/consent-ui/src/app/pis/result-page/result-page.component.ts
+++ b/consent-ui/src/app/pis/result-page/result-page.component.ts
@@ -5,7 +5,6 @@ import { Location } from '@angular/common';
import { SessionService } from '../../common/session.service';
import {
AuthStateConsentAuthorizationService,
- DenyRequest,
SinglePayment,
UpdateConsentAuthorizationService
} from '../../api';
@@ -74,8 +73,6 @@ export class ResultPageComponent implements OnInit {
.denyUsingPOST(
this.authorizationId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
- {} as DenyRequest,
'response'
)
.subscribe((res) => {
@@ -94,7 +91,7 @@ export class ResultPageComponent implements OnInit {
private loadRedirectUri(authId: string, redirectCode: string) {
this.authStateConsentAuthorizationService.authUsingGET(authId, redirectCode, 'response').subscribe((res) => {
console.log(res);
- this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.redirectTo = res.headers.get(ApiHeaders.LOCATION);
});
}
diff --git a/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts b/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts
index 3e953a9002..4fa7340a19 100644
--- a/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts
+++ b/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts
@@ -1,14 +1,14 @@
-import { Component, OnInit } from '@angular/core';
-import { StubUtil } from '../../common/utils/stub-util';
-import { Action } from '../../common/utils/action';
-import { Location } from '@angular/common';
-import { ActivatedRoute } from '@angular/router';
-import { SessionService } from '../../common/session.service';
-import { AuthStateConsentAuthorizationService, DenyRequest, UpdateConsentAuthorizationService } from '../../api';
-import { ApiHeaders } from '../../api/api.headers';
-import { PaymentUtil } from '../common/payment-util';
-import { PisPayment } from '../common/models/pis-payment.model';
-import { combineLatest } from 'rxjs';
+import {Component, OnInit} from '@angular/core';
+import {StubUtil} from '../../common/utils/stub-util';
+import {Action} from '../../common/utils/action';
+import {Location} from '@angular/common';
+import {ActivatedRoute} from '@angular/router';
+import {SessionService} from '../../common/session.service';
+import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api';
+import {ApiHeaders} from '../../api/api.headers';
+import {PaymentUtil} from '../common/payment-util';
+import {PisPayment} from '../common/models/pis-payment.model';
+import {combineLatest} from 'rxjs';
@Component({
selector: 'consent-app-to-aspsp-page',
@@ -56,8 +56,6 @@ export class ToAspspPageComponent implements OnInit {
.denyUsingPOST(
this.authorizationId,
StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs
- StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs
- {} as DenyRequest,
'response'
)
.subscribe((res) => {
@@ -69,7 +67,7 @@ export class ToAspspPageComponent implements OnInit {
this.authStateConsentAuthorizationService
.authUsingGET(this.authorizationId, this.sessionService.getRedirectCode(this.authorizationId), 'response')
.subscribe((res) => {
- this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE));
+ this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
this.redirectTo = res.headers.get(ApiHeaders.LOCATION);
});
}
diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html
new file mode 100644
index 0000000000..29201e5112
--- /dev/null
+++ b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html
@@ -0,0 +1,10 @@
+
+
+
+ Confirm consent on your application
+
+ {{authResponse?.scaMethodSelected.explanation}}
+
+
+
+
diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.scss b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts
new file mode 100644
index 0000000000..e10ee3c429
--- /dev/null
+++ b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts
@@ -0,0 +1,64 @@
+import {Component, OnInit} from '@angular/core';
+import {
+ AuthStateConsentAuthorizationService,
+ ConsentAuth,
+ PsuAuthRequest,
+ UpdateConsentAuthorizationService
+} from "../../api";
+import {of, Subject} from "rxjs";
+import {SessionService} from "../../common/session.service";
+import {ActivatedRoute} from "@angular/router";
+import {delay, repeatWhen, switchMap, takeUntil, tap} from "rxjs/operators";
+import {StubUtil} from "../../common/utils/stub-util";
+import {ApiHeaders} from "../../api/api.headers";
+
+@Component({
+ selector: 'wait-for-decoupled-redirection',
+ templateUrl: './wait-for-decoupled.html',
+ styleUrls: ['./wait-for-decoupled.scss']
+})
+export class WaitForDecoupled implements OnInit {
+ public static ROUTE = 'wait-sca-finalization';
+
+ private readonly POLLING_DELAY_MS = 3000;
+
+ authResponse: ConsentAuth | undefined;
+
+ private authId: string;
+
+ private decoupledCompleted = new Subject();
+
+ constructor(
+ private consentAuthorizationService: UpdateConsentAuthorizationService,
+ private consentStatusService: AuthStateConsentAuthorizationService,
+ private sessionService: SessionService,
+ private activatedRoute: ActivatedRoute
+ ) {
+ const route = this.activatedRoute.snapshot;
+ this.authId = route.parent.params.authId;
+ this.sessionService.setRedirectCode(this.authId, route.queryParams.redirectCode);
+ }
+
+ ngOnInit() {
+ of(true)
+ .pipe(switchMap(_ => this.consentAuthorizationService.embeddedUsingPOST(
+ this.authId,
+ StubUtil.X_REQUEST_ID,
+ this.sessionService.getRedirectCode(this.authId),
+ {} as PsuAuthRequest,
+ 'response'
+ )))
+ .pipe(repeatWhen(completed => completed.pipe(delay(this.POLLING_DELAY_MS))), tap())
+ .pipe(takeUntil(this.decoupledCompleted))
+ .subscribe(res => {
+ if (res.headers.get(ApiHeaders.X_XSRF_TOKEN)) {
+ this.sessionService.setRedirectCode(this.authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN));
+ }
+ this.authResponse = res.body;
+
+ if (res.status === 202) {
+ window.location.href = res.headers.get(ApiHeaders.LOCATION);
+ }
+ });
+ }
+}
diff --git a/consent-ui/src/styles.scss b/consent-ui/src/styles.scss
index f8c14de3f1..a6a47ff132 100644
--- a/consent-ui/src/styles.scss
+++ b/consent-ui/src/styles.scss
@@ -89,6 +89,21 @@ a {
padding-left: 1rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
font-size: 1.125rem;
+
+ }
+ .customDate {
+ width: 12.5rem;
+ padding-top: 18px;
+ margin-left: 1rem;
+ }
+ .customNumber {
+ width: 7rem;
+ padding-top: 18px;
+ margin-left: 1rem;
+ margin-right: 1rem;
+ }
+ .customStrong {
+ padding: 5px 0 0 0px;
}
}
@@ -110,6 +125,10 @@ a {
.form-check {
padding-left: 0;
+ .checkboxLabel {
+ padding: 0 0 0 20.5px;
+ }
+
label {
font-family: var(--fontFamilyMedium);
color: #21242c;
@@ -149,6 +168,11 @@ a {
}
}
+.inputWrapper {
+ //margin-left: 3.375rem;
+ margin-left: 2.03124rem;
+}
+
@media (max-width: 767px) {
.bg-sm-light {
background-color: $COLOR_LIGHT_GREY-10;
diff --git a/docker-compose.yml b/docker-compose.yml
index 6f5f828194..35d464d833 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -31,7 +31,7 @@ services:
- FACADE_ENCRYPTION_KEYSETPATH=/keysetpath/example-keyset.json
- FACADE_URLS_EMBEDDED-UI-BASE-URL=http://localhost:14200
- PROTOCOL_GATEWAY-BASE-URL=http://localhost:18085
- image: "adorsys/open-banking-gateway-open-banking-gateway:0.19.1"
+ image: "adorsys/open-banking-gateway-open-banking-gateway:0.30.0.1"
ports:
- "18085:8085"
volumes:
@@ -46,7 +46,7 @@ services:
environment:
- BACKEND_URL=http://fintech-server:8086
restart: on-failure
- image: "adorsys/open-banking-gateway-fintech-ui:0.19.1"
+ image: "adorsys/open-banking-gateway-fintech-ui:0.30.0.1"
ports:
- "24200:4200"
depends_on:
@@ -61,7 +61,7 @@ services:
- FINTECH-UI_HOST=http://localhost:24200
- image: "adorsys/open-banking-gateway-fintech-server:0.19.1"
+ image: "adorsys/open-banking-gateway-fintech-server:0.30.0.1"
ports:
- "18086:8086"
depends_on:
@@ -73,7 +73,7 @@ services:
environment:
- BACKEND_URL=http://open-banking-gateway:8085
restart: on-failure
- image: "adorsys/open-banking-gateway-consent-ui:0.19.1"
+ image: "adorsys/open-banking-gateway-consent-ui:0.30.0.1"
ports:
- "14200:4200"
depends_on:
@@ -83,7 +83,7 @@ services:
hbci-sandbox-server:
restart: on-failure
- image: "adorsys/open-banking-gateway-hbci-sandbox-server:0.19.1"
+ image: "adorsys/open-banking-gateway-hbci-sandbox-server:0.30.0.1"
ports:
- "18090:8090"
networks:
diff --git a/docs/img/big-picture.png b/docs/img/big-picture.png
new file mode 100644
index 0000000000000000000000000000000000000000..c29f4b6e043817a0c9d84ae58cdfbe90ce0b96ab
GIT binary patch
literal 147788
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