Files
oidc-client-rx/src/config/auth-well-known/auth-well-known-data.service.spec.ts
2025-01-31 05:57:51 +08:00

238 lines
7.8 KiB
TypeScript

import { TestBed } from '@/testing';
import { lastValueFrom, of, throwError } from 'rxjs';
import { vi } from 'vitest';
import { DataService } from '../../api/data.service';
import { LoggerService } from '../../logging/logger.service';
import { createRetriableStream } from '../../testing/create-retriable-stream.helper';
import { mockProvider } from '../../testing/mock';
import { AuthWellKnownDataService } from './auth-well-known-data.service';
import type { AuthWellKnownEndpoints } from './auth-well-known-endpoints';
const DUMMY_WELL_KNOWN_DOCUMENT = {
issuer: 'https://identity-server.test/realms/main',
authorization_endpoint:
'https://identity-server.test/realms/main/protocol/openid-connect/auth',
token_endpoint:
'https://identity-server.test/realms/main/protocol/openid-connect/token',
userinfo_endpoint:
'https://identity-server.test/realms/main/protocol/openid-connect/userinfo',
end_session_endpoint:
'https://identity-server.test/realms/main/master/protocol/openid-connect/logout',
jwks_uri:
'https://identity-server.test/realms/main/protocol/openid-connect/certs',
check_session_iframe:
'https://identity-server.test/realms/main/protocol/openid-connect/login-status-iframe.html',
introspection_endpoint:
'https://identity-server.test/realms/main/protocol/openid-connect/token/introspect',
};
describe('AuthWellKnownDataService', () => {
let service: AuthWellKnownDataService;
let dataService: DataService;
let loggerService: LoggerService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthWellKnownDataService,
mockProvider(DataService),
mockProvider(LoggerService),
],
});
service = TestBed.inject(AuthWellKnownDataService);
loggerService = TestBed.inject(LoggerService);
dataService = TestBed.inject(DataService);
});
it('should create', () => {
expect(service).toBeTruthy();
});
describe('getWellKnownDocument', () => {
it('should add suffix if it does not exist on current URL', async () => {
const dataServiceSpy = vi
.spyOn(dataService, 'get')
.mockReturnValue(of(null));
const urlWithoutSuffix = 'myUrl';
const urlWithSuffix = `${urlWithoutSuffix}/.well-known/openid-configuration`;
await lastValueFrom(
(service as any).getWellKnownDocument(urlWithoutSuffix, {
configId: 'configId1',
})
);
expect(dataServiceSpy).toHaveBeenCalledExactlyOnceWith(urlWithSuffix, {
configId: 'configId1',
});
});
it('should not add suffix if it does exist on current url', async () => {
const dataServiceSpy = vi
.spyOn(dataService, 'get')
.mockReturnValue(of(null));
const urlWithSuffix = 'myUrl/.well-known/openid-configuration';
await lastValueFrom(
(service as any).getWellKnownDocument(urlWithSuffix, {
configId: 'configId1',
})
);
expect(dataServiceSpy).toHaveBeenCalledExactlyOnceWith(urlWithSuffix, {
configId: 'configId1',
});
});
it('should not add suffix if it does exist in the middle of current url', async () => {
const dataServiceSpy = vi
.spyOn(dataService, 'get')
.mockReturnValue(of(null));
const urlWithSuffix =
'myUrl/.well-known/openid-configuration/and/some/more/stuff';
await lastValueFrom(
(service as any).getWellKnownDocument(urlWithSuffix, {
configId: 'configId1',
})
);
expect(dataServiceSpy).toHaveBeenCalledExactlyOnceWith(urlWithSuffix, {
configId: 'configId1',
});
});
it('should use the custom suffix provided in the config', async () => {
const dataServiceSpy = vi
.spyOn(dataService, 'get')
.mockReturnValue(of(null));
const urlWithoutSuffix = 'myUrl';
const urlWithSuffix = `${urlWithoutSuffix}/.well-known/test-openid-configuration`;
await lastValueFrom(
(service as any).getWellKnownDocument(urlWithoutSuffix, {
configId: 'configId1',
authWellknownUrlSuffix: '/.well-known/test-openid-configuration',
})
);
expect(dataServiceSpy).toHaveBeenCalledExactlyOnceWith(urlWithSuffix, {
configId: 'configId1',
authWellknownUrlSuffix: '/.well-known/test-openid-configuration',
});
});
it('should retry once', async () => {
vi.spyOn(dataService, 'get').mockReturnValue(
createRetriableStream(
throwError(() => new Error('one')),
of(DUMMY_WELL_KNOWN_DOCUMENT)
)
);
const res: unknown = await lastValueFrom(
(service as any).getWellKnownDocument('anyurl', {
configId: 'configId1',
})
);
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_WELL_KNOWN_DOCUMENT);
});
it('should retry twice', async () => {
vi.spyOn(dataService, 'get').mockReturnValue(
createRetriableStream(
throwError(() => new Error('one')),
throwError(() => new Error('two')),
of(DUMMY_WELL_KNOWN_DOCUMENT)
)
);
const res: any = await lastValueFrom(
(service as any).getWellKnownDocument('anyurl', {
configId: 'configId1',
})
);
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_WELL_KNOWN_DOCUMENT);
});
it('should fail after three tries', async () => {
vi.spyOn(dataService, 'get').mockReturnValue(
createRetriableStream(
throwError(() => new Error('one')),
throwError(() => new Error('two')),
throwError(() => new Error('three')),
of(DUMMY_WELL_KNOWN_DOCUMENT)
)
);
try {
await lastValueFrom(
(service as any).getWellKnownDocument('anyurl', 'configId')
);
} catch (err: unknown) {
expect(err).toBeTruthy();
}
});
});
describe('getWellKnownEndPointsForConfig', () => {
it('calling internal getWellKnownDocument and maps', async () => {
vi.spyOn(dataService, 'get').mockReturnValue(
of({ jwks_uri: 'jwks_uri' })
);
const spy = vi.spyOn(service as any, 'getWellKnownDocument');
const result = await lastValueFrom(
service.getWellKnownEndPointsForConfig({
configId: 'configId1',
authWellknownEndpointUrl: 'any-url',
})
);
expect(spy).toHaveBeenCalled();
expect((result as any).jwks_uri).toBeUndefined();
expect(result.jwksUri).toBe('jwks_uri');
});
it('throws error and logs if no authwellknownUrl is given', async () => {
const loggerSpy = vi.spyOn(loggerService, 'logError');
const config = {
configId: 'configId1',
authWellknownEndpointUrl: undefined,
};
try {
await lastValueFrom(service.getWellKnownEndPointsForConfig(config));
} catch (error: any) {
expect(loggerSpy).toHaveBeenCalledExactlyOnceWith(
config,
'no authWellknownEndpoint given!'
);
expect(error.message).toEqual('no authWellknownEndpoint given!');
}
});
it('should merge the mapped endpoints with the provided endpoints', async () => {
vi.spyOn(dataService, 'get').mockReturnValue(
of(DUMMY_WELL_KNOWN_DOCUMENT)
);
const expected: AuthWellKnownEndpoints = {
endSessionEndpoint: 'config-endSessionEndpoint',
revocationEndpoint: 'config-revocationEndpoint',
jwksUri: DUMMY_WELL_KNOWN_DOCUMENT.jwks_uri,
};
const result = await lastValueFrom(
service.getWellKnownEndPointsForConfig({
configId: 'configId1',
authWellknownEndpointUrl: 'any-url',
authWellknownEndpoints: {
endSessionEndpoint: 'config-endSessionEndpoint',
revocationEndpoint: 'config-revocationEndpoint',
},
})
);
expect(result).toEqual(expect.objectContaining(expected));
});
});
});