feat: init and fork some code from angular-auth-oidc-client
Some checks are pending
Build, Lint & Test Lib / Built, Lint and Test Library (push) Waiting to run
Build, Lint & Test Lib / Angular latest (push) Blocked by required conditions
Build, Lint & Test Lib / Angular latest & Schematics Job (push) Blocked by required conditions
Build, Lint & Test Lib / Angular latest Standalone & Schematics Job (push) Blocked by required conditions
Build, Lint & Test Lib / Angular 16 & RxJs 6 (push) Blocked by required conditions
Build, Lint & Test Lib / Angular V16 (push) Blocked by required conditions
Docs / Build and Deploy Docs Job (push) Waiting to run
Docs / Close Pull Request Job (push) Waiting to run

This commit is contained in:
2025-01-18 01:05:00 +08:00
parent 276d9fbda8
commit 983254164b
201 changed files with 35689 additions and 0 deletions

View File

@@ -0,0 +1,464 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { of } from 'rxjs';
import { mockProvider } from '../../../test/auto-mock';
import { CheckAuthService } from '../../auth-state/check-auth.service';
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
import { LoggerService } from '../../logging/logger.service';
import { RedirectService } from '../../utils/redirect/redirect.service';
import { UrlService } from '../../utils/url/url.service';
import { LoginResponse } from '../login-response';
import { PopupResult } from '../popup/popup-result';
import { PopUpService } from '../popup/popup.service';
import { ResponseTypeValidationService } from '../response-type-validation/response-type-validation.service';
import { ParLoginService } from './par-login.service';
import { ParResponse } from './par-response';
import { ParService } from './par.service';
describe('ParLoginService', () => {
let service: ParLoginService;
let responseTypeValidationService: ResponseTypeValidationService;
let loggerService: LoggerService;
let authWellKnownService: AuthWellKnownService;
let parService: ParService;
let urlService: UrlService;
let redirectService: RedirectService;
let popupService: PopUpService;
let checkAuthService: CheckAuthService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ParLoginService,
mockProvider(LoggerService),
mockProvider(ResponseTypeValidationService),
mockProvider(UrlService),
mockProvider(RedirectService),
mockProvider(AuthWellKnownService),
mockProvider(PopUpService),
mockProvider(CheckAuthService),
mockProvider(ParService),
],
});
});
beforeEach(() => {
service = TestBed.inject(ParLoginService);
loggerService = TestBed.inject(LoggerService);
responseTypeValidationService = TestBed.inject(
ResponseTypeValidationService
);
authWellKnownService = TestBed.inject(AuthWellKnownService);
parService = TestBed.inject(ParService);
urlService = TestBed.inject(UrlService);
redirectService = TestBed.inject(RedirectService);
popupService = TestBed.inject(PopUpService);
checkAuthService = TestBed.inject(CheckAuthService);
});
it('should create', () => {
expect(service).toBeTruthy();
});
describe('loginPar', () => {
it('does nothing if it has an invalid response type', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(false);
const loggerSpy = spyOn(loggerService, 'logError');
const result = service.loginPar({});
expect(result).toBeUndefined();
expect(loggerSpy).toHaveBeenCalled();
}));
it('calls parService.postParRequest without custom params when no custom params are passed', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
const spy = spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
const result = service.loginPar({
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
});
expect(result).toBeUndefined();
expect(spy).toHaveBeenCalled();
}));
it('calls parService.postParRequest with custom params when custom params are passed', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
const spy = spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
const result = service.loginPar(config, {
customParams: { some: 'thing' },
});
expect(result).toBeUndefined();
expect(spy).toHaveBeenCalledOnceWith(config, {
customParams: { some: 'thing' },
});
}));
it('returns undefined and logs error when no url could be created', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('');
const spy = spyOn(loggerService, 'logError');
const result = service.loginPar(config);
expect(result).toBeUndefined();
expect(spy).toHaveBeenCalledTimes(1);
}));
it('calls redirect service redirectTo when url could be created', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
const authOptions = {};
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('some-par-url');
const spy = spyOn(redirectService, 'redirectTo');
service.loginPar(config, authOptions);
expect(spy).toHaveBeenCalledOnceWith('some-par-url');
}));
it('calls urlHandler when URL is passed', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('some-par-url');
const redirectToSpy = spyOn(redirectService, 'redirectTo');
const spy = jasmine.createSpy();
const urlHandler = (url: any): void => {
spy(url);
};
service.loginPar(config, { urlHandler });
expect(spy).toHaveBeenCalledOnceWith('some-par-url');
expect(redirectToSpy).not.toHaveBeenCalled();
}));
});
describe('loginWithPopUpPar', () => {
it('does nothing if it has an invalid response type', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(false);
const loggerSpy = spyOn(loggerService, 'logError');
const config = {};
const allConfigs = [config];
service.loginWithPopUpPar(config, allConfigs).subscribe({
error: (err) => {
expect(loggerSpy).toHaveBeenCalled();
expect(err.message).toBe('Invalid response type!');
},
});
}));
it('calls parService.postParRequest without custom params when no custom params are passed', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
const spy = spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
service.loginWithPopUpPar(config, allConfigs).subscribe({
error: (err) => {
expect(spy).toHaveBeenCalled();
expect(err.message).toBe(
"Could not create URL with param requestUri: 'url'"
);
},
});
}));
it('calls parService.postParRequest with custom params when custom params are passed', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
const spy = spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
service
.loginWithPopUpPar(config, allConfigs, {
customParams: { some: 'thing' },
})
.subscribe({
error: (err) => {
expect(spy).toHaveBeenCalledOnceWith(config, {
customParams: { some: 'thing' },
});
expect(err.message).toBe(
"Could not create URL with param requestUri: 'url'"
);
},
});
}));
it('returns undefined and logs error when no URL could be created', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('');
const spy = spyOn(loggerService, 'logError');
service
.loginWithPopUpPar(config, allConfigs, {
customParams: { some: 'thing' },
})
.subscribe({
error: (err) => {
expect(err.message).toBe(
"Could not create URL with param requestUri: 'url'"
);
expect(spy).toHaveBeenCalledTimes(1);
},
});
}));
it('calls popupService openPopUp when URL could be created', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('some-par-url');
spyOn(checkAuthService, 'checkAuth').and.returnValue(
of({} as LoginResponse)
);
spyOnProperty(popupService, 'result$').and.returnValue(
of({} as PopupResult)
);
const spy = spyOn(popupService, 'openPopUp');
service.loginWithPopUpPar(config, allConfigs).subscribe(() => {
expect(spy).toHaveBeenCalledOnceWith('some-par-url', undefined, config);
});
}));
it('returns correct properties if URL is received', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
configId: 'configId1',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('some-par-url');
const checkAuthSpy = spyOn(checkAuthService, 'checkAuth').and.returnValue(
of({
isAuthenticated: true,
configId: 'configId1',
idToken: '',
userData: { any: 'userData' },
accessToken: 'anyAccessToken',
})
);
const popupResult: PopupResult = {
userClosed: false,
receivedUrl: 'someUrl',
};
spyOnProperty(popupService, 'result$').and.returnValue(of(popupResult));
service.loginWithPopUpPar(config, allConfigs).subscribe((result) => {
expect(checkAuthSpy).toHaveBeenCalledOnceWith(
config,
allConfigs,
'someUrl'
);
expect(result).toEqual({
isAuthenticated: true,
configId: 'configId1',
idToken: '',
userData: { any: 'userData' },
accessToken: 'anyAccessToken',
});
});
}));
it('returns correct properties if popup was closed by user', waitForAsync(() => {
spyOn(
responseTypeValidationService,
'hasConfigValidResponseType'
).and.returnValue(true);
const config = {
authWellknownEndpointUrl: 'authWellknownEndpoint',
responseType: 'stubValue',
configId: 'configId1',
};
const allConfigs = [config];
spyOn(
authWellKnownService,
'queryAndStoreAuthWellKnownEndPoints'
).and.returnValue(of({}));
spyOn(parService, 'postParRequest').and.returnValue(
of({ requestUri: 'requestUri' } as ParResponse)
);
spyOn(urlService, 'getAuthorizeParUrl').and.returnValue('some-par-url');
const checkAuthSpy = spyOn(checkAuthService, 'checkAuth');
const popupResult = { userClosed: true } as PopupResult;
spyOnProperty(popupService, 'result$').and.returnValue(of(popupResult));
service.loginWithPopUpPar(config, allConfigs).subscribe((result) => {
expect(checkAuthSpy).not.toHaveBeenCalled();
expect(result).toEqual({
isAuthenticated: false,
errorMessage: 'User closed popup',
configId: 'configId1',
idToken: '',
userData: null,
accessToken: '',
});
});
}));
});
});

View File

@@ -0,0 +1,172 @@
import { inject, Injectable } from 'injection-js';
import { Observable, of, throwError } from 'rxjs';
import { switchMap, take } from 'rxjs/operators';
import { AuthOptions } from '../../auth-options';
import { CheckAuthService } from '../../auth-state/check-auth.service';
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { RedirectService } from '../../utils/redirect/redirect.service';
import { UrlService } from '../../utils/url/url.service';
import { LoginResponse } from '../login-response';
import { PopupOptions } from '../popup/popup-options';
import { PopupResult } from '../popup/popup-result';
import { PopUpService } from '../popup/popup.service';
import { ResponseTypeValidationService } from '../response-type-validation/response-type-validation.service';
import { ParResponse } from './par-response';
import { ParService } from './par.service';
@Injectable()
export class ParLoginService {
private readonly loggerService = inject(LoggerService);
private readonly responseTypeValidationService = inject(
ResponseTypeValidationService
);
private readonly urlService = inject(UrlService);
private readonly redirectService = inject(RedirectService);
private readonly authWellKnownService = inject(AuthWellKnownService);
private readonly popupService = inject(PopUpService);
private readonly checkAuthService = inject(CheckAuthService);
private readonly parService = inject(ParService);
loginPar(
configuration: OpenIdConfiguration,
authOptions?: AuthOptions
): void {
if (
!this.responseTypeValidationService.hasConfigValidResponseType(
configuration
)
) {
this.loggerService.logError(configuration, 'Invalid response type!');
return;
}
this.loggerService.logDebug(
configuration,
'BEGIN Authorize OIDC Flow, no auth data'
);
this.authWellKnownService
.queryAndStoreAuthWellKnownEndPoints(configuration)
.pipe(
switchMap(() =>
this.parService.postParRequest(configuration, authOptions)
)
)
.subscribe((response) => {
this.loggerService.logDebug(configuration, 'par response: ', response);
const url = this.urlService.getAuthorizeParUrl(
response.requestUri,
configuration
);
this.loggerService.logDebug(configuration, 'par request url: ', url);
if (!url) {
this.loggerService.logError(
configuration,
`Could not create URL with param ${response.requestUri}: '${url}'`
);
return;
}
if (authOptions?.urlHandler) {
authOptions.urlHandler(url);
} else {
this.redirectService.redirectTo(url);
}
});
}
loginWithPopUpPar(
configuration: OpenIdConfiguration,
allConfigs: OpenIdConfiguration[],
authOptions?: AuthOptions,
popupOptions?: PopupOptions
): Observable<LoginResponse> {
const { configId } = configuration;
if (
!this.responseTypeValidationService.hasConfigValidResponseType(
configuration
)
) {
const errorMessage = 'Invalid response type!';
this.loggerService.logError(configuration, errorMessage);
return throwError(() => new Error(errorMessage));
}
this.loggerService.logDebug(
configuration,
'BEGIN Authorize OIDC Flow with popup, no auth data'
);
return this.authWellKnownService
.queryAndStoreAuthWellKnownEndPoints(configuration)
.pipe(
switchMap(() =>
this.parService.postParRequest(configuration, authOptions)
),
switchMap((response: ParResponse) => {
this.loggerService.logDebug(
configuration,
`par response: ${response}`
);
const url = this.urlService.getAuthorizeParUrl(
response.requestUri,
configuration
);
this.loggerService.logDebug(configuration, 'par request url: ', url);
if (!url) {
const errorMessage = `Could not create URL with param ${response.requestUri}: 'url'`;
this.loggerService.logError(configuration, errorMessage);
return throwError(() => new Error(errorMessage));
}
this.popupService.openPopUp(url, popupOptions, configuration);
return this.popupService.result$.pipe(
take(1),
switchMap((result: PopupResult) => {
const { userClosed, receivedUrl } = result;
if (userClosed) {
return of({
isAuthenticated: false,
errorMessage: 'User closed popup',
userData: null,
idToken: '',
accessToken: '',
configId,
});
}
return this.checkAuthService.checkAuth(
configuration,
allConfigs,
receivedUrl
);
})
);
})
);
}
}

View File

@@ -0,0 +1,4 @@
export interface ParResponse {
requestUri: string;
expiresIn: number;
}

View File

@@ -0,0 +1,204 @@
import { HttpHeaders } from '@angular/common/http';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { mockProvider } from '../../../test/auto-mock';
import { createRetriableStream } from '../../../test/create-retriable-stream.helper';
import { DataService } from '../../api/data.service';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import { UrlService } from '../../utils/url/url.service';
import { ParService } from './par.service';
describe('ParService', () => {
let service: ParService;
let loggerService: LoggerService;
let urlService: UrlService;
let dataService: DataService;
let storagePersistenceService: StoragePersistenceService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
mockProvider(LoggerService),
mockProvider(UrlService),
mockProvider(DataService),
mockProvider(StoragePersistenceService),
],
});
});
beforeEach(() => {
service = TestBed.inject(ParService);
dataService = TestBed.inject(DataService);
loggerService = TestBed.inject(LoggerService);
storagePersistenceService = TestBed.inject(StoragePersistenceService);
urlService = TestBed.inject(UrlService);
});
it('should create', () => {
expect(service).toBeTruthy();
});
describe('postParRequest', () => {
it('throws error if authWellKnownEndPoints does not exist in storage', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of(null)
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue(null);
service.postParRequest({ configId: 'configId1' }).subscribe({
error: (err) => {
expect(err.message).toBe(
'Could not read PAR endpoint because authWellKnownEndPoints are not given'
);
},
});
}));
it('throws error if par endpoint does not exist in storage', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of(null)
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ some: 'thing' });
service.postParRequest({ configId: 'configId1' }).subscribe({
error: (err) => {
expect(err.message).toBe(
'Could not read PAR endpoint from authWellKnownEndpoints'
);
},
});
}));
it('calls data service with correct params', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url123')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
const dataServiceSpy = spyOn(dataService, 'post').and.returnValue(of({}));
service.postParRequest({ configId: 'configId1' }).subscribe(() => {
expect(dataServiceSpy).toHaveBeenCalledOnceWith(
'parEndpoint',
'some-url123',
{ configId: 'configId1' },
jasmine.any(HttpHeaders)
);
});
}));
it('Gives back correct object properties', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url456')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
spyOn(dataService, 'post').and.returnValue(
of({ expires_in: 123, request_uri: 'request_uri' })
);
service.postParRequest({ configId: 'configId1' }).subscribe((result) => {
expect(result).toEqual({ expiresIn: 123, requestUri: 'request_uri' });
});
}));
it('throws error if data service has got an error', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url789')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
spyOn(dataService, 'post').and.returnValue(
throwError(() => new Error('ERROR'))
);
const loggerSpy = spyOn(loggerService, 'logError');
service.postParRequest({ configId: 'configId1' }).subscribe({
error: (err) => {
expect(err.message).toBe(
'There was an error on ParService postParRequest'
);
expect(loggerSpy).toHaveBeenCalledOnceWith(
{ configId: 'configId1' },
'There was an error on ParService postParRequest',
jasmine.any(Error)
);
},
});
}));
it('should retry once', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url456')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
spyOn(dataService, 'post').and.returnValue(
createRetriableStream(
throwError(() => new Error('ERROR')),
of({ expires_in: 123, request_uri: 'request_uri' })
)
);
service.postParRequest({ configId: 'configId1' }).subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(res).toEqual({ expiresIn: 123, requestUri: 'request_uri' });
},
});
}));
it('should retry twice', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url456')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
spyOn(dataService, 'post').and.returnValue(
createRetriableStream(
throwError(() => new Error('ERROR')),
throwError(() => new Error('ERROR')),
of({ expires_in: 123, request_uri: 'request_uri' })
)
);
service.postParRequest({ configId: 'configId1' }).subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(res).toEqual({ expiresIn: 123, requestUri: 'request_uri' });
},
});
}));
it('should fail after three tries', waitForAsync(() => {
spyOn(urlService, 'createBodyForParCodeFlowRequest').and.returnValue(
of('some-url456')
);
spyOn(storagePersistenceService, 'read')
.withArgs('authWellKnownEndPoints', { configId: 'configId1' })
.and.returnValue({ parEndpoint: 'parEndpoint' });
spyOn(dataService, 'post').and.returnValue(
createRetriableStream(
throwError(() => new Error('ERROR')),
throwError(() => new Error('ERROR')),
throwError(() => new Error('ERROR')),
of({ expires_in: 123, request_uri: 'request_uri' })
)
);
service.postParRequest({ configId: 'configId1' }).subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
}));
});
});

View File

@@ -0,0 +1,87 @@
import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Observable, throwError } from 'rxjs';
import { catchError, map, retry, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
import { AuthOptions } from '../../auth-options';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import { UrlService } from '../../utils/url/url.service';
import { ParResponse } from './par-response';
@Injectable()
export class ParService {
private readonly loggerService = inject(LoggerService);
private readonly urlService = inject(UrlService);
private readonly dataService = inject(DataService);
private readonly storagePersistenceService = inject(
StoragePersistenceService
);
postParRequest(
configuration: OpenIdConfiguration,
authOptions?: AuthOptions
): Observable<ParResponse> {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
const authWellKnownEndpoints = this.storagePersistenceService.read(
'authWellKnownEndPoints',
configuration
);
if (!authWellKnownEndpoints) {
return throwError(
() =>
new Error(
'Could not read PAR endpoint because authWellKnownEndPoints are not given'
)
);
}
const parEndpoint = authWellKnownEndpoints.parEndpoint;
if (!parEndpoint) {
return throwError(
() =>
new Error('Could not read PAR endpoint from authWellKnownEndpoints')
);
}
return this.urlService
.createBodyForParCodeFlowRequest(configuration, authOptions)
.pipe(
switchMap((data) => {
return this.dataService
.post(parEndpoint, data, configuration, headers)
.pipe(
retry(2),
map((response: any) => {
this.loggerService.logDebug(
configuration,
'par response: ',
response
);
return {
expiresIn: response.expires_in,
requestUri: response.request_uri,
};
}),
catchError((error) => {
const errorMessage = `There was an error on ParService postParRequest`;
this.loggerService.logError(configuration, errorMessage, error);
return throwError(() => new Error(errorMessage));
})
);
})
);
}
}