Compare commits

...

7 Commits

Author SHA1 Message Date
fe10ed2850 feat: add more auth features and remove auth module
Some checks failed
Build, Lint & Test Lib / Build, Lint and Test Library (push) Has been cancelled
2025-02-07 16:48:38 +08:00
c8c4fc847d doc: fix docs 2025-02-06 04:40:35 +08:00
25a27e1998 ci: fix workflows 2025-02-06 04:34:59 +08:00
57ae2191ae ci: fix workflows 2025-02-06 04:32:45 +08:00
9eb1239842 fix: fix package.json 2025-02-06 04:29:34 +08:00
13886502b6 feat: add basic example 2025-02-06 04:26:07 +08:00
58d7b3c89e build: fix build and add examples 2025-02-05 22:49:38 +08:00
140 changed files with 3068 additions and 3993 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -1,265 +0,0 @@
name: Build, Lint & Test Lib
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Built, Lint and Test Library
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20
- name: Installing Dependencies
run: npm ci
- name: Linting Library
run: npm run lint-lib
- name: Testing Frontend
run: npm run test-lib-ci
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: './coverage/oidc-client-rx/lcov.info'
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
- name: Building Frontend
run: npm run build-lib-prod
- name: Copying essential additional files
run: npm run copy-files
- name: Show files
run: ls
- name: Upload Artefact
uses: actions/upload-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: dist/oidc-client-rx
AngularLatestVersion:
needs: build_job
runs-on: ubuntu-latest
name: Angular latest
steps:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20
- name: Download Artefact
uses: actions/download-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: oidc-client-rx-artefact
- name: Install AngularCLI globally
run: sudo npm install -g @angular/cli
- name: Show ng Version
run: ng version
- name: Create Angular Project
run: sudo ng new oidc-client-rx-test --skip-git
- name: Npm Install & Install Library from local artefact
run: |
sudo cp -R oidc-client-rx-artefact oidc-client-rx-test/
cd oidc-client-rx-test
sudo npm install --unsafe-perm=true
sudo ng add ./oidc-client-rx-artefact --authority-url-or-tenant-id "my-authority-url" --flow-type "OIDC Code Flow PKCE using refresh tokens" --use-local-package=true --skip-confirmation
- name: Test Angular Application
working-directory: ./oidc-client-rx-test
run: npm test -- --watch=false --browsers=ChromeHeadless
- name: Build Angular Application
working-directory: ./oidc-client-rx-test
run: sudo npm run build
AngularLatestVersionWithSchematics:
needs: build_job
runs-on: ubuntu-latest
name: Angular latest & Schematics Job
steps:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20
- name: Download Artefact
uses: actions/download-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: oidc-client-rx-artefact
- name: Install AngularCLI globally
run: sudo npm install -g @angular/cli
- name: Show ng Version
run: ng version
- name: Create Angular Project
run: sudo ng new oidc-client-rx-test --skip-git
- name: Npm Install & Install Library from local artefact
run: |
sudo cp -R oidc-client-rx-artefact oidc-client-rx-test/
cd oidc-client-rx-test
sudo npm install --unsafe-perm=true
sudo ng add ./oidc-client-rx-artefact --authority-url-or-tenant-id "my-authority-url" --flow-type "Default config" --use-local-package=true --skip-confirmation
- name: Test Angular Application
working-directory: ./oidc-client-rx-test
run: npm test -- --watch=false --browsers=ChromeHeadless
- name: Build Angular Application
working-directory: ./oidc-client-rx-test
run: sudo npm run build
AngularLatestVersionWithNgModuleSchematics:
needs: build_job
runs-on: ubuntu-latest
name: Angular latest Standalone & Schematics Job
steps:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20
- name: Download Artefact
uses: actions/download-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: oidc-client-rx-artefact
- name: Install AngularCLI globally
run: sudo npm install -g @angular/cli
- name: Show ng Version
run: ng version
- name: Create Angular Project
run: sudo ng new oidc-client-rx-test --skip-git --standalone=false
- name: Npm Install & Install Library from local artefact
run: |
sudo cp -R oidc-client-rx-artefact oidc-client-rx-test/
cd oidc-client-rx-test
sudo npm install --unsafe-perm=true
sudo ng add ./oidc-client-rx-artefact --authority-url-or-tenant-id "my-authority-url" --flow-type "OIDC Code Flow PKCE using refresh tokens" --use-local-package=true --skip-confirmation
- name: Test Angular Application
working-directory: ./oidc-client-rx-test
run: npm test -- --watch=false --browsers=ChromeHeadless
- name: Build Angular Application
working-directory: ./oidc-client-rx-test
run: sudo npm run build
Angular16VersionWithRxJs6:
needs: build_job
runs-on: ubuntu-latest
name: Angular 16 & RxJs 6
steps:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 20
- name: Download Artefact
uses: actions/download-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: oidc-client-rx-artefact
- name: Install AngularCLI globally
run: sudo npm install -g @angular/cli@16
- name: Show ng Version
run: ng version
- name: Create Angular Project
run: sudo ng new oidc-client-rx-test --skip-git
- name: npm install RxJs 6
working-directory: ./oidc-client-rx-test
run: sudo npm install rxjs@6.5.3
- name: Npm Install & Install Library from local artefact
run: |
sudo cp -R oidc-client-rx-artefact oidc-client-rx-test/
cd oidc-client-rx-test
sudo npm install --unsafe-perm=true
sudo ng add ./oidc-client-rx-artefact --authority-url-or-tenant-id "my-authority-url" --flow-type "OIDC Code Flow PKCE using refresh tokens" --use-local-package=true --skip-confirmation
- name: Test Angular Application
working-directory: ./oidc-client-rx-test
run: npm test -- --watch=false --browsers=ChromeHeadless
- name: Build Angular Application
working-directory: ./oidc-client-rx-test
run: sudo npm run build
LibWithAngularV16:
needs: build_job
runs-on: ubuntu-latest
name: Angular V16
steps:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18
- name: Download Artefact
uses: actions/download-artifact@v3
with:
name: angular_auth_oidc_client_artefact
path: oidc-client-rx-artefact
- name: Install AngularCLI globally
run: sudo npm install -g @angular/cli@16
- name: Show ng Version
run: ng version
- name: Create Angular Project
run: sudo ng new oidc-client-rx-test --skip-git
- name: Npm Install & Install Library from local artefact
run: |
sudo cp -R oidc-client-rx-artefact oidc-client-rx-test/
cd oidc-client-rx-test
sudo npm install --unsafe-perm=true
sudo ng add ./oidc-client-rx-artefact --authority-url-or-tenant-id "my-authority-url" --flow-type "OIDC Code Flow PKCE using refresh tokens" --use-local-package=true --skip-confirmation
- name: Test Angular Application
working-directory: ./oidc-client-rx-test
run: npm test -- --watch=false --browsers=ChromeHeadless
- name: Build Angular Application
working-directory: ./oidc-client-rx-test
run: sudo npm run build

View File

@ -1,61 +0,0 @@
name: Docs
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Docs Job
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 18
- name: Installing Dependencies
run: sudo npm install
- name: Installing Dependencies for docs - in docs folder
run: sudo npm install
working-directory: docs/site/oidc-client-rx
- name: Building Documentation
run: sudo npm run build
working-directory: docs/site/oidc-client-rx
- name: Build And Deploy
if: ${{ github.actor == 'damienbod' || github.actor == 'FabianGosebrink' }}
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: 'upload'
###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
app_location: '/docs/site/oidc-client-rx' # App source code path
app_artifact_location: 'build' # Built app content directory - optional
###### End of Repository/Build Configurations ######
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: 'close'

View File

@ -1,27 +0,0 @@
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm install -g pnpm && pnpm install
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm exec playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30

58
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: Build, Lint & Test Lib
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
workflow_dispatch:
jobs:
build_job:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build, Lint and Test Library
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node and Install Dependencies
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Linting Library
run: npm run lint
- name: Testing Frontend
run: npm run test-ci
- name: 'Report Coverage'
if: (github.event_name == 'pull_request' && github.event.action != 'closed')
uses: davelosert/vitest-coverage-report-action@v2
- name: Building Frontend
run: npm run build
- name: Show files
run: ls
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: oidc_client_rx_artifact
path: dist

4
.gitignore vendored
View File

@ -2,6 +2,7 @@
# compiled output
/dist
**/dist
/tmp
/out-tsc
# Only exists if Bazel was run
@ -45,7 +46,6 @@ testem.log
.DS_Store
Thumbs.db
/.angulardoc.json
debug.log
/.husky
@ -55,3 +55,5 @@ debug.log
/blob-report/
/playwright/.cache/
/.vitest
/.rslib
**/*.tsbuildinfo

View File

@ -2,6 +2,7 @@
<img src="./assets/logo-512.png" height="150" alt="OUTPOSTS">
<div style="color: #232848; font-weight: 700;">OIDC-CLIENT-RX</div>
<div align="center">
<img src="https://img.shields.io/github/actions/workflow/status/lonelyhentxi/oidc-client-rx/build.yml?branch=main" alt="build-status" />
<img src="https://img.shields.io/badge/status-work--in--progress-blue" alt="status-badge" />
</div>
</h1>
@ -10,7 +11,55 @@
## Quick Start
@TODO Coming Soon
@TODO Add More Details
### Install
```sh
pnpm add oidc-client-rx @outposts/injection-js @abraham/reflection
# npm install oidc-client-rx @outposts/injection-js @abraham/reflection
# yarn add oidc-client-rx @outposts/injection-js @abraham/reflection
```
### Basic Usage
```typescript
import '@abraham/reflection'; // or 'reflect-metadata' | 'core-js/es7/reflect'
import { type Injector, ReflectiveInjector } from '@outposts/injection-js';
import { LogLevel, OidcSecurityService, provideAuth, withDefaultFeatures } from 'oidc-client-rx';
const injector = ReflectiveInjector.resolveAndCreate(
provideAuth(
{
config: {
authority: '<your-authority>',
redirectUrl: `${window.location.origin}/auth/callback`,
postLogoutRedirectUri: window.location.origin,
clientId: '<your-client-id>',
scope: 'openid profile email offline_access',
responseType: 'code',
silentRenew: true,
useRefreshToken: true,
logLevel: LogLevel.Debug,
...
},
},
withDefaultFeatures()
)
) as Injector;
const oidcSecurityService = injector.get(OidcSecurityService);
oidcSecurityService.checkAuth().subscribe((result) => {
console.debug('checkAuth result: ', result);
});
const isAuthenticated$ = oidcSecurityService.isAuthenticated$;
```
### More Examples
- [React + TanStack Router](https://github.com/lonelyhentxi/oidc-client-rx/tree/main/examples/react-tanstack-router)
## License

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -6,7 +6,7 @@
"style": {
"noNonNullAssertion": "off",
"noParameterAssign": "off",
"useFilenamingConvention": "warn",
"useFilenamingConvention": "off",
"noParameterProperties": "off"
},
"suspicious": {

View File

@ -0,0 +1,13 @@
# Local
.DS_Store
*.local
*.log*
# Dist
node_modules
dist/
# IDE
.vscode/*
!.vscode/extensions.json
.idea

View File

@ -0,0 +1,29 @@
# Rsbuild project
## Setup
Install the dependencies:
```bash
pnpm install
```
## Get started
Start the dev server:
```bash
pnpm dev
```
Build the app for production:
```bash
pnpm build
```
Preview the production build locally:
```bash
pnpm preview
```

View File

@ -0,0 +1,31 @@
{
"name": "react-tanstack-router",
"private": true,
"version": "1.0.0",
"scripts": {
"dev": "rsbuild dev",
"build": "rsbuild build",
"preview": "rsbuild preview"
},
"dependencies": {
"@abraham/reflection": "^0.12.0",
"@outposts/injection-js": "^2.5.1",
"@tanstack/react-router": "^1.99.6",
"@tanstack/router-devtools": "^1.99.6",
"autoprefixer": "^10.4.20",
"observable-hooks": "^4.2.4",
"oidc-client-rx": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^3.0.0"
},
"devDependencies": {
"@rsbuild/core": "^1.2.3",
"@rsbuild/plugin-react": "^1.1.0",
"@tanstack/router-plugin": "^1.99.6",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"postcss": "^8.5.1",
"typescript": "^5.7.3"
}
}

View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@ -0,0 +1,12 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack';
export default defineConfig({
plugins: [pluginReact()],
tools: {
rspack: {
plugins: [TanStackRouterRspack()],
},
},
});

View File

@ -0,0 +1 @@
/// <reference types="@rsbuild/core/types" />

View File

@ -0,0 +1,87 @@
import '@abraham/reflection'; // or 'reflect-metadata' | 'core-js/es7/reflect'
import { type Injector, ReflectiveInjector } from '@outposts/injection-js';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import {
LogLevel,
OidcSecurityService,
provideAuth,
withDefaultFeatures,
} from 'oidc-client-rx';
import {
InjectorContextVoidInjector,
InjectorProvider,
} from 'oidc-client-rx/adapters/react';
import { withTanstackRouter } from 'oidc-client-rx/adapters/tanstack-router';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { routeTree } from './routeTree.gen';
import './style.css';
// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
context: {
injector: InjectorContextVoidInjector,
oidcSecurityService: {} as OidcSecurityService,
},
});
// Register things for typesafety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router;
}
}
const injector = ReflectiveInjector.resolveAndCreate(
provideAuth(
{
config: {
authority: 'https://k9bor3.logto.app/oidc',
redirectUrl: `${window.location.origin}/auth/callback`,
postLogoutRedirectUri: window.location.origin,
clientId: 'zz5vo27wtvtjf36srwtbp',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
useRefreshToken: true,
logLevel: LogLevel.Debug,
autoUserInfo: true,
renewUserInfoAfterTokenRenew: true,
customParamsAuthRequest: {
prompt: 'consent',
},
},
},
withDefaultFeatures(
// the after feature will replace the before same type feature
// so the following line can be ignored
{ router: { enabled: false } }
),
withTanstackRouter(router)
)
) as Injector;
// if needed, check when init
const oidcSecurityService = injector.get(OidcSecurityService);
oidcSecurityService.checkAuth().subscribe();
const rootEl = document.getElementById('root');
if (rootEl) {
const root = ReactDOM.createRoot(rootEl);
root.render(
<React.StrictMode>
<InjectorProvider injector={injector}>
<RouterProvider
router={router}
context={{ injector, oidcSecurityService }}
/>
</InjectorProvider>
</React.StrictMode>
);
}

View File

@ -0,0 +1,111 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as rootRoute } from './routes/__root';
import { Route as AuthCallbackImport } from './routes/auth/callback';
import { Route as IndexImport } from './routes/index';
// Create/Update Routes
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any);
const AuthCallbackRoute = AuthCallbackImport.update({
id: '/auth/callback',
path: '/auth/callback',
getParentRoute: () => rootRoute,
} as any);
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/';
path: '/';
fullPath: '/';
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
'/auth/callback': {
id: '/auth/callback';
path: '/auth/callback';
fullPath: '/auth/callback';
preLoaderRoute: typeof AuthCallbackImport;
parentRoute: typeof rootRoute;
};
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
'/': typeof IndexRoute;
'/auth/callback': typeof AuthCallbackRoute;
}
export interface FileRoutesByTo {
'/': typeof IndexRoute;
'/auth/callback': typeof AuthCallbackRoute;
}
export interface FileRoutesById {
__root__: typeof rootRoute;
'/': typeof IndexRoute;
'/auth/callback': typeof AuthCallbackRoute;
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: '/' | '/auth/callback';
fileRoutesByTo: FileRoutesByTo;
to: '/' | '/auth/callback';
id: '__root__' | '/' | '/auth/callback';
fileRoutesById: FileRoutesById;
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute;
AuthCallbackRoute: typeof AuthCallbackRoute;
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AuthCallbackRoute: AuthCallbackRoute,
};
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>();
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/auth/callback"
]
},
"/": {
"filePath": "index.tsx"
},
"/auth/callback": {
"filePath": "auth/callback.tsx"
}
}
}
ROUTE_MANIFEST_END */

View File

@ -0,0 +1,38 @@
import type { Injector } from '@outposts/injection-js';
import {
Link,
Outlet,
createRootRouteWithContext,
} from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import type { OidcSecurityService } from 'oidc-client-rx';
export interface RouterContext {
injector: Injector;
oidcSecurityService: OidcSecurityService;
}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
});
function RootComponent() {
return (
<>
<div className="flex gap-2 p-2 text-lg">
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
</div>
<hr />
<Outlet />
<TanStackRouterDevtools position="bottom-right" />
</>
);
}

View File

@ -0,0 +1,13 @@
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/auth/callback')({
component: AuthCallbackComponent,
});
function AuthCallbackComponent() {
return (
<div className="p-2">
<h3>Auth Callback: validating...</h3>
</div>
);
}

View File

@ -0,0 +1,40 @@
import { createFileRoute } from '@tanstack/react-router';
import { useObservableEagerState } from 'observable-hooks';
import { useOidcClient } from 'oidc-client-rx/adapters/react';
import { useCallback } from 'react';
export const Route = createFileRoute('/')({
component: HomeComponent,
});
function HomeComponent() {
const { oidcSecurityService } = useOidcClient();
const { isAuthenticated } = useObservableEagerState(
oidcSecurityService.isAuthenticated$
);
const handleLogin = useCallback(() => {
oidcSecurityService.authorize().subscribe();
}, [oidcSecurityService]);
const handleLogout = useCallback(() => {
oidcSecurityService.logoff().subscribe();
}, [oidcSecurityService]);
return (
<div className="p-2 text-center">
<h1>Welcome OIDC-CLIENT-RX DEMO of react-tanstack-router</h1>
<p>Is authenticated? {isAuthenticated ? 'True' : 'False'}</p>
{isAuthenticated ? (
<button onClick={handleLogout} type="button">
Click to Logout
</button>
) : (
<button onClick={handleLogin} type="button">
Click to Login
</button>
)}
</div>
);
}

View File

@ -0,0 +1,13 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
color-scheme: light dark;
}
* {
@apply border-gray-200 dark:border-gray-800;
}
body {
@apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200;
}

View File

@ -0,0 +1,4 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{js,jsx,ts,tsx}', './index.html'],
};

View File

@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"rootDir": ".",
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true,
"resolveJsonModule": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"emitDeclarationOnly": true,
"noEmit": true,
"outDir": "./dist",
"declarationDir": "./dist",
"jsx": "preserve"
},
"include": ["src"]
}

View File

@ -0,0 +1,4 @@
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts"
}

View File

@ -1,41 +0,0 @@
# EXAMPLE USAGE
# Refer for explanation to following link:
# https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md
#
pre-push:
commands:
fix-prettier:
tags: frontend security
glob: '*.{js,ts}'
run: npm run fix-prettier {staged_files}
pre-commit:
parallel: true
commands:
check-blockwords:
run: npm run check-blockwords
lint:
run: npm run lint-lib
#
# pre-commit:
# parallel: true
# commands:
# eslint:
# glob: "*.{js,ts}"
# run: yarn eslint {staged_files}
# rubocop:
# tags: backend style
# glob: "*.rb"
# exclude: "application.rb|routes.rb"
# run: bundle exec rubocop --force-exclusion {all_files}
# govet:
# tags: backend style
# files: git ls-files -m
# glob: "*.go"
# run: go vet {files}
# scripts:
# "hello.js":
# runner: node
# "any.go":
# runner: go run

View File

@ -1,4 +0,0 @@
/**
* @license oidc-client-rx
* MIT license
*/

3006
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,16 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./adapters/react": {
"types": "./dist/adapters/react/index.d.ts",
"import": "./dist/adapters/react/index.js",
"require": "./dist/adapters/react.cjs"
},
"./adapters/tanstack-router": {
"types": "./dist/adapters/tanstack-router/index.d.ts",
"import": "./dist/adapters/tanstack-router/index.js",
"require": "./dist/adapters/tanstack-router.cjs"
}
},
"main": "./dist/index.cjs",
@ -30,31 +40,31 @@
"test-ci": "vitest --watch=false --coverage",
"pack": "npm run build && npm pack ./dist",
"publish": "npm run build && npm publish ./dist",
"coverage": "vitest run --coverage",
"lint": "ultracite lint",
"format": "ultracite format",
"cli": "tsx scripts/cli.ts"
},
"dependencies": {
"@ngify/http": "^2.0.4",
"injection-js": "git+https://github.com/mgechev/injection-js.git#81a10e0",
"reflect-metadata": "^0.2.2"
"@outposts/injection-js": "^2.5.1"
},
"peerDependencies": {
"@tanstack/react-router": "*",
"react": ">=16.8.0",
"rxjs": "^7.4.0||>=8.0.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@biomejs/js-api": "0.7.1",
"@biomejs/wasm-nodejs": "^1.9.4",
"@evilmartians/lefthook": "^1.0.3",
"@playwright/test": "^1.49.1",
"@rslib/core": "^0.3.1",
"@rslib/core": "^0.4.0",
"@swc/core": "^1.10.12",
"@tanstack/react-router": "^1.99.6",
"@types/jsdom": "^21.1.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.12.0",
"@vitest/browser": "^3.0.4",
"@types/react": "^19.0.8",
"@vitest/coverage-v8": "^3.0.4",
"commander": "^13.1.0",
"jsdom": "^26.0.0",
@ -62,6 +72,8 @@
"oxc-parser": "^0.48.1",
"oxc-walker": "^0.2.2",
"playwright": "^1.50.0",
"react": "^19.0.0",
"reflect-metadata": "^0.2.2",
"rfc4648": "^1.5.0",
"rxjs": "^7.4.0",
"tsx": "^4.19.2",
@ -71,6 +83,14 @@
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.4"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"@tanstack/react-router": {
"optional": true
}
},
"keywords": [
"rxjs",
"oidc",
@ -86,6 +106,19 @@
"certified",
"oauth",
"authorization",
"reactivex"
]
"reactivex",
"injection-js",
"injection"
],
"pnpm": {
"onlyBuiltDependencies": [
"@biomejs/biome",
"@swc/core",
"core-js",
"edgedriver",
"esbuild",
"geckodriver",
"msw"
]
}
}

2012
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
packages:
- 'examples/*'

View File

@ -2,17 +2,31 @@ import { defineConfig } from '@rslib/core';
export default defineConfig({
source: {
tsconfigPath: './tsconfig.lib.json'
tsconfigPath: './tsconfig.lib.json',
},
lib: [
{
format: 'esm',
syntax: 'es2021',
dts: true,
bundle: false,
dts: {
bundle: false,
build: true,
distPath: './dist',
},
},
{
format: 'cjs',
syntax: 'es2021',
dts: false,
bundle: true,
source: {
entry: {
index: './src/index.ts',
'adapters/react': './src/adapters/react/index.ts',
'adapters/tanstack-router': './src/adapters/tanstack-router/index.ts',
},
},
},
],
output: {

View File

@ -0,0 +1,47 @@
import type { InjectionToken, Injector, Type } from '@outposts/injection-js';
import {
type PropsWithChildren,
createContext,
createElement,
useContext,
useMemo,
} from 'react';
import { OidcSecurityService } from '../..';
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({
injector,
...props
}: PropsWithChildren<{ injector: Injector }>) {
return createElement(InjectorContext, {
...props,
value: injector,
});
}
export function useInjector() {
return useContext(InjectorContext);
}
export function useOidcClient() {
const injector = useInjector();
const oidcSecurityService = useMemo(
() => injector.get(OidcSecurityService),
[injector]
);
return {
injector,
oidcSecurityService,
};
}

View File

@ -0,0 +1,41 @@
import { InjectionToken, inject } from '@outposts/injection-js';
import type { Router } from '@tanstack/react-router';
import type { AuthFeature } from '../../features';
import { AbstractRouter } from '../../router';
export type TanStackRouter = Router<any, any, any, any, any, any>;
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: 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,
},
],
};
}

View File

@ -1,17 +1,17 @@
import { TestBed } from '@/testing';
import {
type DefaultHttpTestingController,
HTTP_CLIENT_TEST_CONTROLLER,
provideHttpClientTesting,
} from '@/testing/http';
import { HttpHeaders } from '@ngify/http';
import type { HttpTestingController } from '@ngify/http/testing';
import { ReplaySubject, firstValueFrom, share } from 'rxjs';
import { DataService } from './data.service';
import { HttpBaseService } from './http-base.service';
describe('Data Service', () => {
let dataService: DataService;
let httpMock: HttpTestingController;
let httpMock: DefaultHttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({

View File

@ -1,5 +1,5 @@
import { HttpHeaders } from '@ngify/http';
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { HttpParams } from '../http';

View File

@ -1,11 +1,10 @@
import { HttpClient, type HttpHeaders } from '@ngify/http';
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import type { HttpParams } from '../http';
import { HTTP_CLIENT, type HttpHeaders, type HttpParams } from '../http';
@Injectable()
export class HttpBaseService {
private readonly http = inject(HttpClient);
private readonly http = inject(HTTP_CLIENT);
get<T>(
url: string,
@ -13,7 +12,7 @@ export class HttpBaseService {
): Observable<T> {
return this.http.get<T>(url, {
...options,
params: options.params.toNgify(),
params: options.params?.toNgify(),
});
}
@ -22,9 +21,9 @@ export class HttpBaseService {
body: unknown,
options: { headers?: HttpHeaders; params?: HttpParams } = {}
): Observable<T> {
return this.http.post<T>(url, body, {
return this.http.post<T>(url, body as any, {
...options,
params: options.params.toNgify(),
params: options.params?.toNgify(),
});
}
}

View File

@ -1,4 +1,4 @@
import { InjectionToken, type Provider } from 'injection-js';
import { InjectionToken, type Provider } from '@outposts/injection-js';
import {
type StsConfigLoader,
StsConfigStaticLoader,

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { BehaviorSubject, type Observable, throwError } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, forkJoin, of, throwError } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { AutoLoginService } from '../auto-login/auto-login.service';

View File

@ -1,66 +0,0 @@
import { TestBed } from '@/testing';
import { of } from 'rxjs';
import { PASSED_CONFIG } from './auth-config';
import { AuthModule } from './auth.module';
import { ConfigurationService } from './config/config.service';
import {
StsConfigHttpLoader,
StsConfigLoader,
StsConfigStaticLoader,
} from './config/loader/config-loader';
import { mockProvider } from './testing/mock';
describe('AuthModule', () => {
describe('APP_CONFIG', () => {
let authModule: AuthModule;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AuthModule.forRoot({ config: { authority: 'something' } })],
providers: [mockProvider(ConfigurationService)],
}).compileComponents();
authModule = TestBed.getImportByType(AuthModule);
});
it('should create', () => {
expect(AuthModule).toBeDefined();
expect(authModule).toBeDefined();
});
it('should provide config', () => {
const config = authModule.get(PASSED_CONFIG);
expect(config).toEqual({ config: { authority: 'something' } });
});
it('should create StsConfigStaticLoader if config is passed', () => {
const configLoader = authModule.get(StsConfigLoader);
expect(configLoader instanceof StsConfigStaticLoader).toBe(true);
});
});
describe('StsConfigHttpLoader', () => {
let authModule: AuthModule;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
AuthModule.forRoot({
loader: {
provide: StsConfigLoader,
useFactory: () => new StsConfigHttpLoader(of({})),
},
}),
],
providers: [mockProvider(ConfigurationService)],
}).compileComponents();
authModule = TestBed.getImportByType(AuthModule);
});
it('should create StsConfigStaticLoader if config is passed', () => {
const configLoader = authModule.get(StsConfigLoader);
expect(configLoader instanceof StsConfigHttpLoader).toBe(true);
});
});
});

View File

@ -1,41 +0,0 @@
import {
type InjectionToken,
Injector,
ReflectiveInjector,
type Type,
} from 'injection-js';
import type { PassedInitialConfig } from './auth-config';
import type { Module } from './injection';
import { _provideAuth } from './provide-auth';
export interface AuthModuleOptions {
passedConfig: PassedInitialConfig;
parentInjector?: ReflectiveInjector;
}
export class AuthModule extends Injector {
passedConfig: PassedInitialConfig;
injector: ReflectiveInjector;
parentInjector?: Injector;
constructor(passedConfig?: PassedInitialConfig, parentInjector?: Injector) {
super();
this.passedConfig = passedConfig ?? {};
this.parentInjector = parentInjector;
this.injector = ReflectiveInjector.resolveAndCreate(
[..._provideAuth(this.passedConfig)],
this.parentInjector
);
}
static forRoot(passedConfig?: PassedInitialConfig): Module {
return (parentInjector?: Injector) =>
new AuthModule(passedConfig, parentInjector);
}
get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T): T;
get(token: any, notFoundValue?: any);
get(token: unknown, notFoundValue?: unknown): any {
return this.injector.get(token, notFoundValue);
}
}

View File

@ -1,4 +1,4 @@
import { TestBed, mockRouterProvider } from '@/testing';
import { type MockRouter, TestBed, mockRouterProvider } from '@/testing';
import {
AbstractRouter,
type ActivatedRouteSnapshot,
@ -43,7 +43,7 @@ describe('AutoLoginPartialRoutesGuard', () => {
let storagePersistenceService: StoragePersistenceService;
let configurationService: ConfigurationService;
let autoLoginService: AutoLoginService;
let router: AbstractRouter;
let router: MockRouter;
beforeEach(() => {
authStateService = TestBed.inject(AuthStateService);
@ -293,11 +293,6 @@ describe('AutoLoginPartialRoutesGuard', () => {
extractedUrl: router.parseUrl(
'some-url12/with/some-param?queryParam=true'
),
extras: {},
id: 1,
initialUrl: router.parseUrl(''),
previousNavigation: null,
trigger: 'imperative',
});
await firstValueFrom(guard.canLoad());
@ -342,7 +337,7 @@ describe('AutoLoginPartialRoutesGuard', () => {
let storagePersistenceService: StoragePersistenceService;
let configurationService: ConfigurationService;
let autoLoginService: AutoLoginService;
let router: AbstractRouter;
let router: MockRouter;
beforeEach(() => {
authStateService = TestBed.inject(AuthStateService);
@ -398,11 +393,6 @@ describe('AutoLoginPartialRoutesGuard', () => {
extractedUrl: router.parseUrl(
'some-url12/with/some-param?queryParam=true'
),
extras: {},
id: 1,
initialUrl: router.parseUrl(''),
previousNavigation: null,
trigger: 'imperative',
});
vi.spyOn(authStateService, 'areAuthStorageTokensValid').mockReturnValue(

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import type { AuthOptions } from '../auth-options';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { injectAbstractType } from '../injection';
import { AbstractRouter } from '../router';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { Observable, Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, type Subscription, interval } from 'rxjs';
import { DOCUMENT } from '../dom';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, ReplaySubject, forkJoin, of, throwError } from 'rxjs';
import { catchError, map, share, switchMap } from 'rxjs/operators';
import { AuthStateService } from '../auth-state/auth-state.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import {
type Observable,
TimeoutError,

View File

@ -1,4 +1,4 @@
import { inject, Injectable } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { map, retry } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
@ -42,7 +42,7 @@ export class AuthWellKnownDataService {
introspectionEndpoint: wellKnownEndpoints.introspection_endpoint,
parEndpoint:
wellKnownEndpoints.pushed_authorization_request_endpoint,
} as AuthWellKnownEndpoints)
}) as AuthWellKnownEndpoints
),
map((mappedWellKnownEndpoints) => ({
...mappedWellKnownEndpoints,

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { EventTypes } from '../../public-events/event-types';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, forkJoin, of } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { injectAbstractType } from '../injection/inject';

View File

@ -1,4 +1,4 @@
import type { Provider } from 'injection-js';
import type { Provider } from '@outposts/injection-js';
import { type Observable, forkJoin, of } from 'rxjs';
import { map } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../openid-configuration';

View File

@ -1,4 +1,4 @@
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
import { TestBed } from '@/testing';
import { mockImplementationWhenArgs, spyOnWithOrigin } from '@/testing/spy';
import { vi } from 'vitest';
import { LogLevel } from '../../logging/log-level';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { LoggerService } from '../../logging/logger.service';
import type { OpenIdConfiguration } from '../openid-configuration';
import type { Level, RuleValidationResult } from './rule';

View File

@ -1,3 +1,3 @@
import { InjectionToken } from "injection-js";
import { InjectionToken } from '@outposts/injection-js';
export const DOCUMENT = new InjectionToken<Document>('document');
export const DOCUMENT = new InjectionToken<Document>('document');

View File

@ -1,4 +1,4 @@
import { Injectable } from 'injection-js';
import { Injectable } from '@outposts/injection-js';
@Injectable()
export class JwkExtractor {

131
src/features.ts Normal file
View File

@ -0,0 +1,131 @@
import type { HttpFeature } from '@ngify/http';
import type { Provider } from '@outposts/injection-js';
import { DOCUMENT } from './dom';
import { provideHttpClient } from './http';
import {
AbstractRouter,
VanillaHistoryRouter,
VanillaLocationRouter,
} from './router';
import { AbstractSecurityStorage } from './storage/abstract-security-storage';
import { DefaultLocalStorageService } from './storage/default-localstorage.service';
import { DefaultSessionStorageService } from './storage/default-sessionstorage.service';
import { PLATFORM_ID } from './utils/platform-provider/platform.provider';
/**
* A feature to be used with `provideAuth`.
*/
export interface AuthFeature {
ɵproviders: Provider[];
}
export interface BrowserPlatformFeatureOptions {
enabled?: boolean;
}
export function withBrowserPlatform({
enabled = true,
}: BrowserPlatformFeatureOptions = {}): AuthFeature {
return {
ɵproviders: enabled
? [
{
provide: DOCUMENT,
useFactory: () => document,
},
{
provide: PLATFORM_ID,
useValue: 'browser',
},
]
: [],
};
}
export interface HttpClientFeatureOptions {
enabled?: boolean;
features?: HttpFeature[];
}
export function withHttpClient({
features,
enabled = true,
}: HttpClientFeatureOptions = {}): AuthFeature {
return {
ɵproviders: enabled ? provideHttpClient(features) : [],
};
}
export type SecurityStorageType = 'session-storage' | 'local-storage';
export interface SecurityStorageFeatureOptions {
enabled?: boolean;
type?: SecurityStorageType;
}
export function withSecurityStorage({
enabled = true,
type = 'session-storage',
}: SecurityStorageFeatureOptions = {}): AuthFeature {
return {
ɵproviders: enabled
? [
type === 'session-storage'
? {
provide: AbstractSecurityStorage,
useClass: DefaultLocalStorageService,
}
: {
provide: AbstractSecurityStorage,
useClass: DefaultSessionStorageService,
},
]
: [],
};
}
export type VanillaRouterType = 'location' | 'history';
export interface VanillaRouterFeatureOptions {
enabled?: boolean;
type?: VanillaRouterType;
}
export function withVanillaRouter({
enabled = true,
type = 'history',
}: VanillaRouterFeatureOptions = {}): AuthFeature {
return {
ɵproviders: enabled
? [
type === 'location'
? {
provide: AbstractRouter,
useClass: VanillaLocationRouter,
}
: {
provide: AbstractRouter,
useClass: VanillaHistoryRouter,
},
]
: [],
};
}
export interface DefaultFeaturesOptions {
browserPlatform?: BrowserPlatformFeatureOptions;
securityStorage?: SecurityStorageFeatureOptions;
router?: VanillaRouterFeatureOptions;
httpClient?: HttpClientFeatureOptions;
}
export function withDefaultFeatures(
options: DefaultFeaturesOptions = {}
): AuthFeature[] {
return [
withBrowserPlatform(options.browserPlatform),
withSecurityStorage(options.securityStorage),
withHttpClient(options.httpClient),
withVanillaRouter(options.router),
].filter(Boolean) as AuthFeature[];
}

View File

@ -1,5 +1,5 @@
import { HttpHeaders } from '@ngify/http';
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError, timer } from 'rxjs';
import { catchError, mergeMap, retryWhen, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { catchError, switchMap, tap } from 'rxjs/operators';
import { AuthStateService } from '../../auth-state/auth-state.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of } from 'rxjs';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { DOCUMENT } from '../../dom';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { AuthStateService } from '../../auth-state/auth-state.service';
import type { OpenIdConfiguration } from '../../config/openid-configuration';

View File

@ -1,5 +1,5 @@
import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError, timer } from 'rxjs';
import { catchError, mergeMap, retryWhen, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthStateService } from '../../auth-state/auth-state.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { AuthStateService } from '../../auth-state/auth-state.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { LoggerService } from '../logging/logger.service';
import { StoragePersistenceService } from '../storage/storage-persistence.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import { concatMap } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { CryptoService } from '../../utils/crypto/crypto.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { AuthStateService } from '../auth-state/auth-state.service';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { LoggerService } from '../logging/logger.service';

View File

@ -1,5 +1,5 @@
import { HttpResponse } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { DataService } from '../api/data.service';

View File

@ -1,16 +1,29 @@
import {
// biome-ignore lint/nursery/noExportedImports: <explanation>
HttpClient as DefaultHttpClient,
type HttpBackend,
HttpClient,
type HttpFeature,
HttpFeatureKind,
// biome-ignore lint/nursery/noExportedImports: <explanation>
HttpHeaders,
type HttpInterceptor,
type HttpInterceptorFn,
type HttpRequest,
type HttpParams as NgifyHttpParams,
withInterceptors,
withLegacyInterceptors,
} from '@ngify/http';
import { InjectionToken, Optional, type Provider } from 'injection-js';
import {
InjectionToken,
Optional,
type Provider,
} from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import type { ArrayOrNullableOne } from '../utils/types';
export { HttpParams, HttpParamsOptions } from './params';
// biome-ignore lint/nursery/noExportedImports: <explanation>
import { HttpParams, type HttpParamsOptions } from './params';
export { HttpParams, HttpHeaders, type HttpParamsOptions, DefaultHttpClient };
export const HTTP_FEATURES = new InjectionToken<HttpFeature[]>('HTTP_FEATURES');
@ -108,14 +121,29 @@ export function provideHttpClient(features: HttpFeature[] = []): Provider[] {
multi: true,
},
{
provide: HttpClient,
provide: HTTP_CLIENT,
useFactory: (features: ArrayOrNullableOne<HttpFeature>[]) => {
const normalizedFeatures = [features]
.flat(Number.MAX_SAFE_INTEGER)
.filter(Boolean) as HttpFeature[];
return new HttpClient(...normalizedFeatures);
return new DefaultHttpClient(...normalizedFeatures);
},
deps: [HTTP_FEATURES],
},
];
}
export type HttpClient = {
get<T>(
url: string,
options?: { headers?: HttpHeaders; params?: NgifyHttpParams }
): Observable<T>;
post<T>(
url: string,
body?: HttpRequest<any>['body'],
options?: { headers?: HttpHeaders; params?: NgifyHttpParams }
): Observable<T>;
};
export const HTTP_CLIENT = new InjectionToken<HttpClient>('HTTP_CLIENT');

View File

@ -88,6 +88,7 @@ function paramParser(
// The `window.location.search` can be used while creating an instance of the `HttpParams` class
// (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search`
// may start with the `?` char, so we strip it if it's present.
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
const params: string[] = rawParams.replace(/^\?/, '').split('&');
params.forEach((param: string) => {
const eqIdx = param.indexOf('=');
@ -304,7 +305,7 @@ export class HttpParams {
toNgify(): NgifyHttpParams {
this.init();
return new NgifyHttpParams().appendAll(
Object.fromEntries(this.map.entries())
Object.fromEntries(this.map!.entries())
);
}

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { take } from 'rxjs/operators';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { DOCUMENT } from '../dom';
import { LoggerService } from '../logging/logger.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import {
Observable,
ReplaySubject,
@ -82,14 +82,14 @@ export class RefreshSessionIframeService {
): void {
const instanceId = Math.random();
const oidcSilentRenewInit$ = fromEventPattern(
const oidcSilentRenewInit$ = fromEventPattern<CustomEvent>(
(handler) =>
this.document.defaultView.window.addEventListener(
this.document.defaultView?.window?.addEventListener(
'oidc-silent-renew-init',
handler
),
(handler) =>
this.document.defaultView.window.removeEventListener(
this.document.defaultView?.window?.removeEventListener(
'oidc-silent-renew-init',
handler
)
@ -104,12 +104,12 @@ export class RefreshSessionIframeService {
}
this.silentRenewEventHandlerSubscription = fromEventPattern<CustomEvent>(
(handler) =>
this.document.defaultView.window.addEventListener(
this.document.defaultView?.window?.addEventListener(
'oidc-silent-renew-message',
handler
),
(handler) =>
this.document.defaultView.window.removeEventListener(
this.document.defaultView?.window?.removeEventListener(
'oidc-silent-renew-message',
handler
)

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, Subject, of, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AuthStateService } from '../auth-state/auth-state.service';

View File

@ -4,7 +4,6 @@ export type { PassedInitialConfig } from './auth-config';
export * from './auth-options';
export * from './auth-state/auth-result';
export * from './auth-state/auth-state';
export * from './auth.module';
export * from './auto-login/auto-login-partial-routes.guard';
export * from './config/auth-well-known/auth-well-known-endpoints';
export * from './config/config.service';
@ -31,3 +30,4 @@ export * from './validation/validation-result';
export * from './injection';
export * from './router';
export * from './http';
export * from './features';

View File

@ -1,7 +0,0 @@
import { InjectionToken } from 'injection-js';
import type { Observable } from 'rxjs';
export const APP_INITIALIZER = new InjectionToken<
// biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
readonly (() => void | Observable<unknown> | Promise<unknown>)[]
>('APP_INITIALIZER');

View File

@ -1,3 +1,2 @@
export type { Module } from './module';
export { APP_INITIALIZER } from './convention';
export { injectAbstractType } from './inject';

View File

@ -1,4 +1,4 @@
import { inject } from 'injection-js';
import { inject } from '@outposts/injection-js';
// biome-ignore lint/complexity/noBannedTypes: <explanation>
export interface AbstractType<T> extends Function {

View File

@ -1,3 +1,3 @@
import type { Injector } from 'injection-js';
import type { Injector } from '@outposts/injection-js';
export type Module = (parentInjector: Injector) => Injector;

View File

@ -1,11 +1,15 @@
import { TestBed } from '@/testing';
import {
type DefaultHttpTestingController,
HTTP_CLIENT_TEST_CONTROLLER,
TestBed,
provideHttpClientTesting,
} from '@/testing';
import { HttpClient } from '@ngify/http';
import type { HttpTestingController } from '@ngify/http/testing';
import { HTTP_INTERCEPTOR_FNS, HTTP_LEGACY_INTERCEPTORS } from 'oidc-client-rx';
import {
type DefaultHttpClient,
HTTP_CLIENT,
HTTP_INTERCEPTOR_FNS,
HTTP_LEGACY_INTERCEPTORS,
} from 'oidc-client-rx';
import { ReplaySubject, firstValueFrom, share } from 'rxjs';
import { vi } from 'vitest';
import { AuthStateService } from '../auth-state/auth-state.service';
@ -16,9 +20,9 @@ import { AuthInterceptor, authInterceptor } from './auth.interceptor';
import { ClosestMatchingRouteService } from './closest-matching-route.service';
describe('AuthHttpInterceptor', () => {
let httpTestingController: HttpTestingController;
let httpTestingController: DefaultHttpTestingController;
let configurationService: ConfigurationService;
let httpClient: HttpClient;
let httpClient: DefaultHttpClient;
let authStateService: AuthStateService;
let closestMatchingRouteService: ClosestMatchingRouteService;
@ -40,7 +44,7 @@ describe('AuthHttpInterceptor', () => {
],
});
httpClient = TestBed.inject(HttpClient);
httpClient = TestBed.inject(HTTP_CLIENT) as DefaultHttpClient;
httpTestingController = TestBed.inject(HTTP_CLIENT_TEST_CONTROLLER);
configurationService = TestBed.inject(ConfigurationService);
authStateService = TestBed.inject(AuthStateService);
@ -72,7 +76,7 @@ describe('AuthHttpInterceptor', () => {
],
});
httpClient = TestBed.inject(HttpClient);
httpClient = TestBed.inject(HTTP_CLIENT) as DefaultHttpClient;
httpTestingController = TestBed.inject(HTTP_CLIENT_TEST_CONTROLLER);
configurationService = TestBed.inject(ConfigurationService);
authStateService = TestBed.inject(AuthStateService);

View File

@ -6,7 +6,7 @@ import type {
HttpInterceptorFn,
HttpRequest,
} from '@ngify/http';
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs';
import { AuthStateService } from '../auth-state/auth-state.service';
import { ConfigurationService } from '../config/config.service';

View File

@ -1,4 +1,4 @@
import { Injectable } from 'injection-js';
import { Injectable } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../config/openid-configuration';
@Injectable()

View File

@ -1,4 +1,4 @@
import { Injectable } from 'injection-js';
import { Injectable } from '@outposts/injection-js';
/**
* Implement this class-interface to create a custom logger service.

View File

@ -1,4 +1,4 @@
import { Injectable } from 'injection-js';
import { Injectable } from '@outposts/injection-js';
import type { AbstractLoggerService } from './abstract-logger.service';
@Injectable()

View File

@ -1,4 +1,4 @@
import { Injectable } from 'injection-js';
import { Injectable } from '@outposts/injection-js';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { injectAbstractType } from '../injection/inject';
import { AbstractLoggerService } from './abstract-logger.service';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import type { AuthOptions } from '../auth-options';
import type { OpenIdConfiguration } from '../config/openid-configuration';

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import type { AuthOptions } from '../../auth-options';

View File

@ -1,5 +1,5 @@
import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, map, retry, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
@ -74,7 +74,8 @@ export class ParService {
};
}),
catchError((error) => {
const errorMessage = 'There was an error on ParService postParRequest';
const errorMessage =
'There was an error on ParService postParRequest';
this.loggerService.logError(configuration, errorMessage, error);

View File

@ -1,4 +1,4 @@
import { Injectable, inject } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators';
import type { AuthOptions } from '../../auth-options';

View File

@ -1,7 +1,7 @@
import { DOCUMENT } from '../../dom';
import { inject, Injectable } from 'injection-js';
import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, Subject } from 'rxjs';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { DOCUMENT } from '../../dom';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import type { PopupOptions } from './popup-options';

Some files were not shown because too many files have changed in this diff Show More