Compare commits
8 Commits
fe10ed2850
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e662d7d123 | |||
| dff1e1f9a6 | |||
| 3aabcd6442 | |||
| 0d957dfb1c | |||
| 144e4c2f97 | |||
| de07175ff4 | |||
| 41f2b04c45 | |||
| ba13828093 |
@@ -7,11 +7,11 @@ import {
|
|||||||
provideAuth,
|
provideAuth,
|
||||||
withDefaultFeatures,
|
withDefaultFeatures,
|
||||||
} from 'oidc-client-rx';
|
} from 'oidc-client-rx';
|
||||||
|
import { withTanstackRouter } from 'oidc-client-rx/adapters/@tanstack/react-router';
|
||||||
import {
|
import {
|
||||||
InjectorContextVoidInjector,
|
InjectorContextVoidInjector,
|
||||||
InjectorProvider,
|
InjectorProvider,
|
||||||
} from 'oidc-client-rx/adapters/react';
|
} from 'oidc-client-rx/adapters/react';
|
||||||
import { withTanstackRouter } from 'oidc-client-rx/adapters/tanstack-router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import { routeTree } from './routeTree.gen';
|
import { routeTree } from './routeTree.gen';
|
||||||
|
|||||||
50
package.json
50
package.json
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "oidc-client-rx",
|
"name": "oidc-client-rx",
|
||||||
|
"version": "0.1.0-alpha.8",
|
||||||
"homepage": "https://github.com/lonelyhentxi/oidc-client-rx",
|
"homepage": "https://github.com/lonelyhentxi/oidc-client-rx",
|
||||||
"author": "lonelyhentxi",
|
"author": "lonelyhentxi",
|
||||||
"description": "ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications",
|
"description": "ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications",
|
||||||
@@ -10,7 +11,6 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/lonelyhentxi/oidc-client-rx/issues"
|
"url": "https://github.com/lonelyhentxi/oidc-client-rx/issues"
|
||||||
},
|
},
|
||||||
"version": "0.1.0",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
@@ -23,59 +23,71 @@
|
|||||||
"import": "./dist/adapters/react/index.js",
|
"import": "./dist/adapters/react/index.js",
|
||||||
"require": "./dist/adapters/react.cjs"
|
"require": "./dist/adapters/react.cjs"
|
||||||
},
|
},
|
||||||
"./adapters/tanstack-router": {
|
"./adapters/solid-js": {
|
||||||
"types": "./dist/adapters/tanstack-router/index.d.ts",
|
"types": "./dist/adapters/solid-js/index.d.ts",
|
||||||
"import": "./dist/adapters/tanstack-router/index.js",
|
"import": "./dist/adapters/solid-js/index.js",
|
||||||
"require": "./dist/adapters/tanstack-router.cjs"
|
"require": "./dist/adapters/solid-js.cjs"
|
||||||
|
},
|
||||||
|
"./adapters/@tanstack/react-router": {
|
||||||
|
"types": "./dist/adapters/@tanstack/react-router.d.ts",
|
||||||
|
"import": "./dist/adapters/@tanstack/react-router.js",
|
||||||
|
"require": "./dist/adapters/@tanstack/react-router.cjs"
|
||||||
|
},
|
||||||
|
"./adapters/@tanstack/solid-router": {
|
||||||
|
"types": "./dist/adapters/@tanstack/solid-router.d.ts",
|
||||||
|
"import": "./dist/adapters/@tanstack/solid-router.js",
|
||||||
|
"require": "./dist/adapters/@tanstack/solid-router.cjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"files": ["dist"],
|
"files": ["dist", "licenses", "LICENSE", "README.md"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rslib build",
|
"build": "rslib build",
|
||||||
"dev": "rslib build --watch",
|
"dev": "rslib build --watch",
|
||||||
"test": "vitest --coverage",
|
"test": "vitest --coverage",
|
||||||
"test-ci": "vitest --watch=false --coverage",
|
"test-ci": "vitest --watch=false --coverage",
|
||||||
"pack": "npm run build && npm pack ./dist",
|
"prepublishOnly": "npm run build",
|
||||||
"publish": "npm run build && npm publish ./dist",
|
|
||||||
"lint": "ultracite lint",
|
"lint": "ultracite lint",
|
||||||
"format": "ultracite format",
|
"format": "ultracite format",
|
||||||
"cli": "tsx scripts/cli.ts"
|
"cli": "tsx scripts/cli.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ngify/http": "^2.0.4",
|
"@ngify/http": "^2.0.4",
|
||||||
"@outposts/injection-js": "^2.5.1"
|
"@outposts/injection-js": "^2.5.1",
|
||||||
|
"rfc4648": "^1.5.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tanstack/react-router": "*",
|
"@tanstack/react-router": "*",
|
||||||
|
"@tanstack/solid-router": "*",
|
||||||
"react": ">=16.8.0",
|
"react": ">=16.8.0",
|
||||||
"rxjs": "^7.4.0||>=8.0.0"
|
"rxjs": "^7.4.0||>=8.0.0",
|
||||||
|
"solid-js": "^1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@biomejs/js-api": "0.7.1",
|
"@biomejs/js-api": "0.7.1",
|
||||||
"@biomejs/wasm-nodejs": "^1.9.4",
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.49.1",
|
||||||
"@rslib/core": "^0.4.0",
|
"@rslib/core": "^0.5.3",
|
||||||
"@swc/core": "^1.10.12",
|
"@swc/core": "^1.10.12",
|
||||||
"@tanstack/react-router": "^1.99.6",
|
"@tanstack/react-router": "^1.112.11",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@tanstack/solid-router": "^1.112.11",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^22.12.0",
|
"@types/node": "^22.12.0",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.0.8",
|
||||||
"@vitest/coverage-v8": "^3.0.4",
|
"@vitest/coverage-v8": "^3.0.4",
|
||||||
"commander": "^13.1.0",
|
"commander": "^13.1.0",
|
||||||
"jsdom": "^26.0.0",
|
"happy-dom": "^17.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oxc-parser": "^0.48.1",
|
"oxc-parser": "^0.54.0",
|
||||||
"oxc-walker": "^0.2.2",
|
"oxc-walker": "^0.2.2",
|
||||||
"playwright": "^1.50.0",
|
"playwright": "^1.50.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rfc4648": "^1.5.0",
|
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
|
"solid-js": "^1.9.5",
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"ultracite": "^4.1.15",
|
"ultracite": "^4.1.15",
|
||||||
@@ -89,6 +101,12 @@
|
|||||||
},
|
},
|
||||||
"@tanstack/react-router": {
|
"@tanstack/react-router": {
|
||||||
"optional": true
|
"optional": true
|
||||||
|
},
|
||||||
|
"@tanstack/solid-router": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"solid-js": {
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
829
pnpm-lock.yaml
generated
829
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,14 @@ export default defineConfig({
|
|||||||
bundle: false,
|
bundle: false,
|
||||||
dts: {
|
dts: {
|
||||||
bundle: false,
|
bundle: false,
|
||||||
build: true,
|
build: false,
|
||||||
distPath: './dist',
|
distPath: './dist',
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
entry: {
|
||||||
|
index: ['src/**/*.ts', '!**/*.spec.ts', '!src/testing/**/*'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
@@ -24,7 +29,11 @@ export default defineConfig({
|
|||||||
entry: {
|
entry: {
|
||||||
index: './src/index.ts',
|
index: './src/index.ts',
|
||||||
'adapters/react': './src/adapters/react/index.ts',
|
'adapters/react': './src/adapters/react/index.ts',
|
||||||
'adapters/tanstack-router': './src/adapters/tanstack-router/index.ts',
|
'adapters/solid-js': './src/adapters/solid-js/index.ts',
|
||||||
|
'adapters/@tanstack/react-router':
|
||||||
|
'./src/adapters/@tanstack/react-router.ts',
|
||||||
|
'adapters/@tanstack/solid-router':
|
||||||
|
'./src/adapters/@tanstack/solid-router.ts',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { InjectionToken, inject } from '@outposts/injection-js';
|
import { InjectionToken, inject } from '@outposts/injection-js';
|
||||||
import type { Router } from '@tanstack/react-router';
|
import type { AnyRouter } from '@tanstack/react-router';
|
||||||
import type { AuthFeature } from '../../features';
|
import type { AuthFeature } from '../../features';
|
||||||
import { AbstractRouter } from '../../router';
|
import { AbstractRouter, ROUTER_ABS_PATH_PATTERN } from '../../router';
|
||||||
|
|
||||||
export type TanStackRouter = Router<any, any, any, any, any, any>;
|
export type TanStackRouter = AnyRouter;
|
||||||
|
|
||||||
export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>(
|
export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>(
|
||||||
'TANSTACK_ROUTER'
|
'TANSTACK_ROUTER'
|
||||||
@@ -14,7 +14,7 @@ export class TanStackRouterAdapter implements AbstractRouter<string> {
|
|||||||
|
|
||||||
navigateByUrl(url: string): void {
|
navigateByUrl(url: string): void {
|
||||||
this.router.navigate({
|
this.router.navigate({
|
||||||
href: url,
|
href: ROUTER_ABS_PATH_PATTERN.test(url) ? url : `/${url}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
41
src/adapters/@tanstack/solid-router.ts
Normal file
41
src/adapters/@tanstack/solid-router.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { InjectionToken, inject } from '@outposts/injection-js';
|
||||||
|
import type { AnyRouter } from '@tanstack/solid-router';
|
||||||
|
import type { AuthFeature } from '../../features';
|
||||||
|
import { AbstractRouter, ROUTER_ABS_PATH_PATTERN } from '../../router';
|
||||||
|
|
||||||
|
export type TanStackRouter = AnyRouter;
|
||||||
|
|
||||||
|
export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>(
|
||||||
|
'TANSTACK_ROUTER'
|
||||||
|
);
|
||||||
|
|
||||||
|
export class TanStackRouterAdapter implements AbstractRouter<string> {
|
||||||
|
private router = inject(TANSTACK_ROUTER);
|
||||||
|
|
||||||
|
navigateByUrl(url: string): void {
|
||||||
|
this.router.navigate({
|
||||||
|
href: ROUTER_ABS_PATH_PATTERN.test(url) ? url : `/${url}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentNavigation() {
|
||||||
|
return {
|
||||||
|
extractedUrl: this.router.state.location.href,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withTanstackRouter(router: TanStackRouter): AuthFeature {
|
||||||
|
return {
|
||||||
|
ɵproviders: [
|
||||||
|
{
|
||||||
|
provide: TANSTACK_ROUTER,
|
||||||
|
useValue: router,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AbstractRouter,
|
||||||
|
useClass: TanStackRouterAdapter,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { OidcSecurityService } from '../..';
|
import { OidcSecurityService } from '../../oidc.security.service';
|
||||||
|
|
||||||
export const InjectorContextVoidInjector: Injector = {
|
export const InjectorContextVoidInjector: Injector = {
|
||||||
get: <T>(_token: Type<T> | InjectionToken<T>, _notFoundValue?: T): T => {
|
get: <T>(_token: Type<T> | InjectionToken<T>, _notFoundValue?: T): T => {
|
||||||
|
|||||||
43
src/adapters/solid-js/index.ts
Normal file
43
src/adapters/solid-js/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import type { InjectionToken, Injector, Type } from '@outposts/injection-js';
|
||||||
|
import {
|
||||||
|
type FlowProps,
|
||||||
|
createContext,
|
||||||
|
createMemo,
|
||||||
|
mergeProps,
|
||||||
|
splitProps,
|
||||||
|
useContext,
|
||||||
|
} from 'solid-js';
|
||||||
|
import { OidcSecurityService } from '../../oidc.security.service';
|
||||||
|
|
||||||
|
export const InjectorContextVoidInjector: Injector = {
|
||||||
|
get: <T>(_token: Type<T> | InjectionToken<T>, _notFoundValue?: T): T => {
|
||||||
|
throw new Error('Please wrap with a InjectorContext.Provider first');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InjectorContext = createContext<Injector>(
|
||||||
|
InjectorContextVoidInjector
|
||||||
|
);
|
||||||
|
|
||||||
|
export function InjectorProvider(props: FlowProps<{ injector: Injector }>) {
|
||||||
|
const [local, others] = splitProps(props, ['injector']);
|
||||||
|
const providerProps = mergeProps(others, { value: local.injector });
|
||||||
|
return InjectorContext.Provider(providerProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useInjector() {
|
||||||
|
return useContext(InjectorContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOidcClient() {
|
||||||
|
const injector = useInjector();
|
||||||
|
|
||||||
|
const oidcSecurityService = createMemo(() =>
|
||||||
|
injector.get(OidcSecurityService)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
injector,
|
||||||
|
oidcSecurityService: oidcSecurityService(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable, inject } from '@outposts/injection-js';
|
import { Injectable, inject } from '@outposts/injection-js';
|
||||||
import type { Observable } from 'rxjs';
|
import { type Observable, of } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import type { AuthOptions } from '../auth-options';
|
import type { AuthOptions } from '../auth-options';
|
||||||
import { AuthStateService } from '../auth-state/auth-state.service';
|
import { AuthStateService } from '../auth-state/auth-state.service';
|
||||||
import { ConfigurationService } from '../config/config.service';
|
import { ConfigurationService } from '../config/config.service';
|
||||||
@@ -126,7 +126,7 @@ function checkAuth(
|
|||||||
configId?: string
|
configId?: string
|
||||||
): Observable<boolean> {
|
): Observable<boolean> {
|
||||||
return configurationService.getOpenIDConfiguration(configId).pipe(
|
return configurationService.getOpenIDConfiguration(configId).pipe(
|
||||||
map((configuration) => {
|
switchMap((configuration) => {
|
||||||
const isAuthenticated =
|
const isAuthenticated =
|
||||||
authStateService.areAuthStorageTokensValid(configuration);
|
authStateService.areAuthStorageTokensValid(configuration);
|
||||||
|
|
||||||
@@ -137,13 +137,16 @@ function checkAuth(
|
|||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
autoLoginService.saveRedirectRoute(configuration, url);
|
autoLoginService.saveRedirectRoute(configuration, url);
|
||||||
if (authOptions) {
|
if (authOptions) {
|
||||||
loginService.login(configuration, authOptions);
|
return loginService
|
||||||
} else {
|
.login(configuration, authOptions)
|
||||||
loginService.login(configuration);
|
.pipe(switchMap(() => of(isAuthenticated)));
|
||||||
}
|
}
|
||||||
|
return loginService
|
||||||
|
.login(configuration)
|
||||||
|
.pipe(switchMap(() => of(isAuthenticated)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return isAuthenticated;
|
return of(isAuthenticated);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ export interface OpenIdConfiguration {
|
|||||||
* or if it contains additional audiences not trusted by the Client.
|
* or if it contains additional audiences not trusted by the Client.
|
||||||
*/
|
*/
|
||||||
clientId?: string;
|
clientId?: string;
|
||||||
|
/**
|
||||||
|
* @dangerous
|
||||||
|
* @see [client secret is missing](https://github.com/damienbod/angular-auth-oidc-client/issues/399)
|
||||||
|
* The client secret. For some oidc service the must provide this.
|
||||||
|
*/
|
||||||
|
clientSecret?: string;
|
||||||
/**
|
/**
|
||||||
* `code`, `id_token token` or `id_token`.
|
* `code`, `id_token token` or `id_token`.
|
||||||
* Name of the flow which can be configured.
|
* Name of the flow which can be configured.
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { HttpFeature } from '@ngify/http';
|
import type { HttpFeature } from '@ngify/http';
|
||||||
import type { Provider } from '@outposts/injection-js';
|
import type { Provider } from '@outposts/injection-js';
|
||||||
import { DOCUMENT } from './dom';
|
import { DOCUMENT } from '../dom';
|
||||||
import { provideHttpClient } from './http';
|
import { provideHttpClient } from '../http';
|
||||||
import {
|
import {
|
||||||
AbstractRouter,
|
AbstractRouter,
|
||||||
VanillaHistoryRouter,
|
VanillaHistoryRouter,
|
||||||
VanillaLocationRouter,
|
VanillaLocationRouter,
|
||||||
} from './router';
|
} from '../router';
|
||||||
import { AbstractSecurityStorage } from './storage/abstract-security-storage';
|
import { AbstractSecurityStorage } from '../storage/abstract-security-storage';
|
||||||
import { DefaultLocalStorageService } from './storage/default-localstorage.service';
|
import { DefaultLocalStorageService } from '../storage/default-localstorage.service';
|
||||||
import { DefaultSessionStorageService } from './storage/default-sessionstorage.service';
|
import { DefaultSessionStorageService } from '../storage/default-sessionstorage.service';
|
||||||
import { PLATFORM_ID } from './utils/platform-provider/platform.provider';
|
import { PLATFORM_ID } from '../utils/platform-provider/platform.provider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A feature to be used with `provideAuth`.
|
* A feature to be used with `provideAuth`.
|
||||||
8
src/features/index.ts
Normal file
8
src/features/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type * from './core';
|
||||||
|
export * from './core';
|
||||||
|
export {
|
||||||
|
CHECK_AUTH_RESULT_EVENT,
|
||||||
|
withCheckAuthResultEvent,
|
||||||
|
type CheckAuthResultEventType,
|
||||||
|
type WithCheckAuthResultEventProps,
|
||||||
|
} from './with-check-auth-result-event';
|
||||||
45
src/features/with-check-auth-result-event.ts
Normal file
45
src/features/with-check-auth-result-event.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { InjectionToken, inject } from '@outposts/injection-js';
|
||||||
|
import { type Observable, filter, shareReplay } from 'rxjs';
|
||||||
|
import { EventTypes } from '../public-events/event-types';
|
||||||
|
import { PublicEventsService } from '../public-events/public-events.service';
|
||||||
|
import type { AuthFeature } from './core';
|
||||||
|
|
||||||
|
export type CheckAuthResultEventType =
|
||||||
|
| { type: EventTypes.CheckingAuthFinished }
|
||||||
|
| {
|
||||||
|
type: EventTypes.CheckingAuthFinishedWithError;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CHECK_AUTH_RESULT_EVENT = new InjectionToken<
|
||||||
|
Observable<CheckAuthResultEventType>
|
||||||
|
>('CHECK_AUTH_RESULT_EVENT');
|
||||||
|
|
||||||
|
export interface WithCheckAuthResultEventProps {
|
||||||
|
shareReplayCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withCheckAuthResultEvent({
|
||||||
|
shareReplayCount = 1,
|
||||||
|
}: WithCheckAuthResultEventProps = {}): AuthFeature {
|
||||||
|
return {
|
||||||
|
ɵproviders: [
|
||||||
|
{
|
||||||
|
provide: CHECK_AUTH_RESULT_EVENT,
|
||||||
|
useFactory: () => {
|
||||||
|
const publishEventService = inject(PublicEventsService);
|
||||||
|
|
||||||
|
return publishEventService.registerForEvents().pipe(
|
||||||
|
filter(
|
||||||
|
(e) =>
|
||||||
|
e.type === EventTypes.CheckingAuthFinishedWithError ||
|
||||||
|
e.type === EventTypes.CheckingAuthFinished
|
||||||
|
),
|
||||||
|
shareReplay(shareReplayCount)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
deps: [PublicEventsService],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -61,12 +61,11 @@ describe('RefreshSessionIframeService ', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
(refreshSessionIframeService as any).initSilentRenewRequest();
|
(refreshSessionIframeService as any).initSilentRenewRequest();
|
||||||
|
expect(dispatchEventSpy).toHaveBeenCalledOnce();
|
||||||
expect(dispatchEventSpy).toHaveBeenCalledExactlyOnceWith(
|
expect(dispatchEventSpy.mock.calls[0][0]).toBeInstanceOf(CustomEvent);
|
||||||
new CustomEvent('oidc-silent-renew-init', {
|
expect(
|
||||||
detail: expect.any(Number),
|
(dispatchEventSpy.mock.calls[0][0] as CustomEvent).detail
|
||||||
})
|
).toBeTypeOf('number');
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, inject } from '@outposts/injection-js';
|
import { Injectable, inject } from '@outposts/injection-js';
|
||||||
import { type Observable, of, throwError } from 'rxjs';
|
import { BehaviorSubject, type Observable, of, throwError } from 'rxjs';
|
||||||
|
import { MockUtil } from 'src/utils/reflect';
|
||||||
import type { AuthOptions } from '../auth-options';
|
import type { AuthOptions } from '../auth-options';
|
||||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
|
import type { OpenIdConfiguration } from '../config/openid-configuration';
|
||||||
import { StoragePersistenceService } from '../storage/storage-persistence.service';
|
import { StoragePersistenceService } from '../storage/storage-persistence.service';
|
||||||
@@ -24,6 +25,7 @@ export class LoginService {
|
|||||||
|
|
||||||
private readonly popupService = inject(PopUpService);
|
private readonly popupService = inject(PopUpService);
|
||||||
|
|
||||||
|
@MockUtil({ implementation: () => new BehaviorSubject(undefined) })
|
||||||
login(
|
login(
|
||||||
configuration: OpenIdConfiguration | null,
|
configuration: OpenIdConfiguration | null,
|
||||||
authOptions?: AuthOptions
|
authOptions?: AuthOptions
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ export abstract class AbstractRouter<
|
|||||||
abstract getCurrentNavigation(): NAVIGATION;
|
abstract getCurrentNavigation(): NAVIGATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VanillaLocationRouter extends AbstractRouter {
|
export const ROUTER_ABS_PATH_PATTERN = /^\//;
|
||||||
private document = inject(DOCUMENT);
|
|
||||||
|
export class VanillaLocationRouter extends AbstractRouter<string> {
|
||||||
|
protected document = inject(DOCUMENT);
|
||||||
|
|
||||||
private get location(): Location {
|
private get location(): Location {
|
||||||
const location = this.document.defaultView?.window?.location;
|
const location = this.document.defaultView?.window?.location;
|
||||||
@@ -42,7 +44,7 @@ export class VanillaLocationRouter extends AbstractRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigateByUrl(url: string): void {
|
navigateByUrl(url: string): void {
|
||||||
this.location.href = url;
|
this.location.href = ROUTER_ABS_PATH_PATTERN.test(url) ? url : `/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentNavigation() {
|
getCurrentNavigation() {
|
||||||
@@ -72,7 +74,11 @@ export class VanillaHistoryRouter extends AbstractRouter<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigateByUrl(url: string): void {
|
navigateByUrl(url: string): void {
|
||||||
this.history.pushState({}, '', url);
|
this.history.pushState(
|
||||||
|
{},
|
||||||
|
'',
|
||||||
|
ROUTER_ABS_PATH_PATTERN.test(url) ? url : `/${url}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentNavigation() {
|
getCurrentNavigation() {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { TestBed } from '@/testing';
|
|||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { DefaultLocalStorageService } from './default-localstorage.service';
|
import { DefaultLocalStorageService } from './default-localstorage.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if use jsdom, then use Storage.prototype, https://github.com/jsdom/jsdom/issues/2318
|
||||||
|
*/
|
||||||
|
|
||||||
describe('DefaultLocalStorageService', () => {
|
describe('DefaultLocalStorageService', () => {
|
||||||
let service: DefaultLocalStorageService;
|
let service: DefaultLocalStorageService;
|
||||||
|
|
||||||
@@ -18,8 +22,7 @@ describe('DefaultLocalStorageService', () => {
|
|||||||
|
|
||||||
describe('read', () => {
|
describe('read', () => {
|
||||||
it('should call localstorage.getItem', () => {
|
it('should call localstorage.getItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(localStorage, 'getItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'getItem');
|
|
||||||
|
|
||||||
service.read('henlo');
|
service.read('henlo');
|
||||||
|
|
||||||
@@ -29,8 +32,7 @@ describe('DefaultLocalStorageService', () => {
|
|||||||
|
|
||||||
describe('write', () => {
|
describe('write', () => {
|
||||||
it('should call localstorage.setItem', () => {
|
it('should call localstorage.setItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(localStorage, 'setItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'setItem');
|
|
||||||
|
|
||||||
service.write('henlo', 'furiend');
|
service.write('henlo', 'furiend');
|
||||||
|
|
||||||
@@ -40,8 +42,7 @@ describe('DefaultLocalStorageService', () => {
|
|||||||
|
|
||||||
describe('remove', () => {
|
describe('remove', () => {
|
||||||
it('should call localstorage.removeItem', () => {
|
it('should call localstorage.removeItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(localStorage, 'removeItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'removeItem');
|
|
||||||
|
|
||||||
service.remove('henlo');
|
service.remove('henlo');
|
||||||
|
|
||||||
@@ -51,8 +52,7 @@ describe('DefaultLocalStorageService', () => {
|
|||||||
|
|
||||||
describe('clear', () => {
|
describe('clear', () => {
|
||||||
it('should call localstorage.clear', () => {
|
it('should call localstorage.clear', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(localStorage, 'clear');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'clear');
|
|
||||||
|
|
||||||
service.clear();
|
service.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { TestBed } from '@/testing';
|
|||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { DefaultSessionStorageService } from './default-sessionstorage.service';
|
import { DefaultSessionStorageService } from './default-sessionstorage.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if use jsdom, then use Storage.prototype, https://github.com/jsdom/jsdom/issues/2318
|
||||||
|
*/
|
||||||
describe('DefaultSessionStorageService', () => {
|
describe('DefaultSessionStorageService', () => {
|
||||||
let service: DefaultSessionStorageService;
|
let service: DefaultSessionStorageService;
|
||||||
|
|
||||||
@@ -18,8 +21,7 @@ describe('DefaultSessionStorageService', () => {
|
|||||||
|
|
||||||
describe('read', () => {
|
describe('read', () => {
|
||||||
it('should call sessionstorage.getItem', () => {
|
it('should call sessionstorage.getItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(sessionStorage, 'getItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'getItem');
|
|
||||||
|
|
||||||
service.read('henlo');
|
service.read('henlo');
|
||||||
|
|
||||||
@@ -29,8 +31,7 @@ describe('DefaultSessionStorageService', () => {
|
|||||||
|
|
||||||
describe('write', () => {
|
describe('write', () => {
|
||||||
it('should call sessionstorage.setItem', () => {
|
it('should call sessionstorage.setItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(sessionStorage, 'setItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'setItem');
|
|
||||||
|
|
||||||
service.write('henlo', 'furiend');
|
service.write('henlo', 'furiend');
|
||||||
|
|
||||||
@@ -40,8 +41,7 @@ describe('DefaultSessionStorageService', () => {
|
|||||||
|
|
||||||
describe('remove', () => {
|
describe('remove', () => {
|
||||||
it('should call sessionstorage.removeItem', () => {
|
it('should call sessionstorage.removeItem', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(sessionStorage, 'removeItem');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'removeItem');
|
|
||||||
|
|
||||||
service.remove('henlo');
|
service.remove('henlo');
|
||||||
|
|
||||||
@@ -51,8 +51,7 @@ describe('DefaultSessionStorageService', () => {
|
|||||||
|
|
||||||
describe('clear', () => {
|
describe('clear', () => {
|
||||||
it('should call sessionstorage.clear', () => {
|
it('should call sessionstorage.clear', () => {
|
||||||
// https://github.com/jsdom/jsdom/issues/2318
|
const spy = vi.spyOn(sessionStorage, 'clear');
|
||||||
const spy = vi.spyOn(Storage.prototype, 'clear');
|
|
||||||
|
|
||||||
service.clear();
|
service.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
import type { Provider } from '@outposts/injection-js';
|
import type { Provider } from '@outposts/injection-js';
|
||||||
import { JSDOM } from 'jsdom';
|
import {
|
||||||
import { AbstractRouter, type Navigation, type UrlTree } from 'oidc-client-rx';
|
AbstractRouter,
|
||||||
|
type UrlTree,
|
||||||
|
VanillaLocationRouter,
|
||||||
|
} from 'oidc-client-rx';
|
||||||
|
|
||||||
export class MockRouter extends AbstractRouter {
|
export class MockRouter extends VanillaLocationRouter {
|
||||||
dom = new JSDOM('', {
|
|
||||||
url: 'http://localhost',
|
|
||||||
});
|
|
||||||
|
|
||||||
navigation: Navigation = {
|
|
||||||
extractedUrl: this.parseUrl(this.dom.window.location.href),
|
|
||||||
};
|
|
||||||
|
|
||||||
navigateByUrl(url: string): void {
|
|
||||||
this.dom.reconfigure({
|
|
||||||
url: new URL(url, this.dom.window.location.href).href,
|
|
||||||
});
|
|
||||||
this.navigation = {
|
|
||||||
extractedUrl: this.parseUrl(this.dom.window.location.href),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
getCurrentNavigation(): Navigation {
|
|
||||||
return this.navigation;
|
|
||||||
}
|
|
||||||
parseUrl(url: string): UrlTree {
|
parseUrl(url: string): UrlTree {
|
||||||
const u = new URL(url, this.dom.window.location.href);
|
const u = new URL(url, this.document.baseURI);
|
||||||
return `${u.pathname}${u.search}${u.hash}`;
|
return `${u.pathname}${u.search}${u.hash}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ export class UrlService {
|
|||||||
configuration: OpenIdConfiguration
|
configuration: OpenIdConfiguration
|
||||||
): string | null {
|
): string | null {
|
||||||
const clientId = this.getClientId(configuration);
|
const clientId = this.getClientId(configuration);
|
||||||
|
const clientSecret = this.getClientSecret(configuration);
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
return null;
|
return null;
|
||||||
@@ -259,6 +260,9 @@ export class UrlService {
|
|||||||
params = params.set('client_id', clientId);
|
params = params.set('client_id', clientId);
|
||||||
params = params.set('token', token);
|
params = params.set('token', token);
|
||||||
params = params.set('token_type_hint', 'access_token');
|
params = params.set('token_type_hint', 'access_token');
|
||||||
|
if (clientSecret) {
|
||||||
|
params = params.set('client_secret', clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
return params.toString();
|
return params.toString();
|
||||||
}
|
}
|
||||||
@@ -268,6 +272,7 @@ export class UrlService {
|
|||||||
configuration: OpenIdConfiguration
|
configuration: OpenIdConfiguration
|
||||||
): string | null {
|
): string | null {
|
||||||
const clientId = this.getClientId(configuration);
|
const clientId = this.getClientId(configuration);
|
||||||
|
const clientSecret = this.getClientSecret(configuration);
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
return null;
|
return null;
|
||||||
@@ -278,6 +283,9 @@ export class UrlService {
|
|||||||
params = params.set('client_id', clientId);
|
params = params.set('client_id', clientId);
|
||||||
params = params.set('token', token);
|
params = params.set('token', token);
|
||||||
params = params.set('token_type_hint', 'refresh_token');
|
params = params.set('token_type_hint', 'refresh_token');
|
||||||
|
if (clientSecret) {
|
||||||
|
params = params.set('client_secret', clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
return params.toString();
|
return params.toString();
|
||||||
}
|
}
|
||||||
@@ -304,6 +312,7 @@ export class UrlService {
|
|||||||
customTokenParams?: { [p: string]: string | number | boolean }
|
customTokenParams?: { [p: string]: string | number | boolean }
|
||||||
): string | null {
|
): string | null {
|
||||||
const clientId = this.getClientId(configuration);
|
const clientId = this.getClientId(configuration);
|
||||||
|
const clientSecret = this.getClientSecret(configuration);
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
return null;
|
return null;
|
||||||
@@ -313,6 +322,9 @@ export class UrlService {
|
|||||||
|
|
||||||
params = params.set('grant_type', 'authorization_code');
|
params = params.set('grant_type', 'authorization_code');
|
||||||
params = params.set('client_id', clientId);
|
params = params.set('client_id', clientId);
|
||||||
|
if (clientSecret) {
|
||||||
|
params = params.set('client_secret', clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
if (!configuration.disablePkce) {
|
if (!configuration.disablePkce) {
|
||||||
const codeVerifier = this.flowsDataService.getCodeVerifier(configuration);
|
const codeVerifier = this.flowsDataService.getCodeVerifier(configuration);
|
||||||
@@ -364,6 +376,7 @@ export class UrlService {
|
|||||||
customParamsRefresh?: { [key: string]: string | number | boolean }
|
customParamsRefresh?: { [key: string]: string | number | boolean }
|
||||||
): string | null {
|
): string | null {
|
||||||
const clientId = this.getClientId(configuration);
|
const clientId = this.getClientId(configuration);
|
||||||
|
const clientSecret = this.getClientSecret(configuration);
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
return null;
|
return null;
|
||||||
@@ -374,6 +387,9 @@ export class UrlService {
|
|||||||
params = params.set('grant_type', 'refresh_token');
|
params = params.set('grant_type', 'refresh_token');
|
||||||
params = params.set('client_id', clientId);
|
params = params.set('client_id', clientId);
|
||||||
params = params.set('refresh_token', refreshToken);
|
params = params.set('refresh_token', refreshToken);
|
||||||
|
if (clientSecret) {
|
||||||
|
params = params.set('client_secret', clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
if (customParamsRefresh) {
|
if (customParamsRefresh) {
|
||||||
params = this.appendCustomParams({ ...customParamsRefresh }, params);
|
params = this.appendCustomParams({ ...customParamsRefresh }, params);
|
||||||
@@ -851,6 +867,18 @@ export class UrlService {
|
|||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getClientSecret(configuration: OpenIdConfiguration): string | null {
|
||||||
|
const { clientSecret } = configuration;
|
||||||
|
|
||||||
|
if (!clientSecret) {
|
||||||
|
this.loggerService.logDebug(configuration, 'could not get clientSecret');
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
private appendCustomParams(
|
private appendCustomParams(
|
||||||
customParams: { [key: string]: string | number | boolean },
|
customParams: { [key: string]: string | number | boolean },
|
||||||
params: HttpParams
|
params: HttpParams
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
|||||||
cacheDir: '.vitest',
|
cacheDir: '.vitest',
|
||||||
test: {
|
test: {
|
||||||
setupFiles: ['src/testing/init-test.ts'],
|
setupFiles: ['src/testing/init-test.ts'],
|
||||||
environment: 'jsdom',
|
environment: 'happy-dom',
|
||||||
include: ['src/**/*.spec.ts'],
|
include: ['src/**/*.spec.ts'],
|
||||||
globals: true,
|
globals: true,
|
||||||
restoreMocks: true,
|
restoreMocks: true,
|
||||||
@@ -15,6 +15,14 @@ export default defineConfig({
|
|||||||
reporter: ['text', 'json-summary', 'json'],
|
reporter: ['text', 'json-summary', 'json'],
|
||||||
// If you want a coverage reports even if your tests are failing, include the reportOnFailure option
|
// If you want a coverage reports even if your tests are failing, include the reportOnFailure option
|
||||||
reportOnFailure: true,
|
reportOnFailure: true,
|
||||||
|
exclude: [
|
||||||
|
'vitest.config.ts',
|
||||||
|
'playwright.config.ts',
|
||||||
|
'rslib.config.ts',
|
||||||
|
'scripts/**',
|
||||||
|
'examples/**',
|
||||||
|
'dist/**',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
Reference in New Issue
Block a user