feat: add basic example

This commit is contained in:
master 2025-02-06 04:26:07 +08:00
parent 58d7b3c89e
commit 13886502b6
18 changed files with 1568 additions and 111 deletions

View File

@ -11,17 +11,54 @@
## Quick Start
@TODO Add More Detailed Informations
@TODO Add More Details
### Install
```sh
# or yarn/npm
pnpm install oidc-client-rx
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
```
```ts
import {} from ''
### 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 } 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,
...
},
}
)
) 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

@ -8,16 +8,24 @@
"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"
"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",
"oidc-client-rx": "workspace:*",
"postcss": "^8.5.1",
"typescript": "^5.7.3"
}
}

View File

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

View File

@ -1,6 +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

@ -1,26 +0,0 @@
body {
margin: 0;
color: #fff;
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
background-image: linear-gradient(to bottom, #020917, #101725);
}
.content {
display: flex;
min-height: 100vh;
line-height: 1.1;
text-align: center;
flex-direction: column;
justify-content: center;
}
.content h1 {
font-size: 3.6rem;
font-weight: 700;
}
.content p {
font-size: 1.2rem;
font-weight: 400;
opacity: 0.5;
}

View File

@ -1,15 +0,0 @@
import { useOidcClient } from 'oidc-client-rx/adapters/react';
import './App.css';
const App = () => {
const { oidcSecurityService } = useOidcClient();
return (
<div className="content">
<h1>Rsbuild with React</h1>
<p>Start building amazing things with Rsbuild.</p>
</div>
);
};
export default App;

View File

@ -1,39 +1,76 @@
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 } from 'oidc-client-rx';
import { InjectorProvider } from 'oidc-client-rx/adapters/react';
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 App from './App';
import { routeTree } from './routeTree.gen';
const rootEl = document.getElementById('root');
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;
}
}
if (rootEl) {
const injector = ReflectiveInjector.resolveAndCreate(
provideAuth({
provideAuth(
{
config: {
authority: '<your authority address here>',
redirectUrl: window.location.origin,
authority: 'https://k9bor3.logto.app/oidc',
redirectUrl: `${window.location.origin}/auth/callback`,
postLogoutRedirectUri: window.location.origin,
clientId: '<your clientId>',
scope: 'openid profile email offline_access',
clientId: 'zz5vo27wtvtjf36srwtbp',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
useRefreshToken: true,
logLevel: LogLevel.Debug,
autoUserInfo: true,
renewUserInfoAfterTokenRenew: true,
customParamsAuthRequest: {
prompt: 'consent',
},
})
},
},
withTanstackRouter(router)
)
) as Injector;
// if needed, check when init
const oidcSecurityService = injector.get(OidcSecurityService);
oidcSecurityService.checkAuthMultiple();
oidcSecurityService.checkAuth().subscribe();
const rootEl = document.getElementById('root');
if (rootEl) {
const root = ReactDOM.createRoot(rootEl);
root.render(
<React.StrictMode>
<InjectorProvider injector={injector}>
<App />
<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 IndexImport } from './routes/index'
import { Route as AuthCallbackImport } from './routes/auth/callback'
// 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,4 @@
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts"
}

1132
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,9 @@
import { InjectionToken, inject } from '@outposts/injection-js';
import type {
AnyRoute,
Router,
TrailingSlashOption,
} from '@tanstack/react-router';
import type { Router } from '@tanstack/react-router';
import { AbstractRouter } from 'src/router';
import type { AuthFeature } from '../../provide-auth';
export type TanStackRouter = Router<AnyRoute, TrailingSlashOption, boolean>;
export type TanStackRouter = Router<any, any, any, any, any, any>;
export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>(
'TANSTACK_ROUTER'

View File

@ -1,14 +1,79 @@
import { HttpClient } from '@ngify/http';
import type { Provider } from '@outposts/injection-js';
import { DataService } from './api/data.service';
import { HttpBaseService } from './api/http-base.service';
import {
PASSED_CONFIG,
type PassedInitialConfig,
createStaticLoader,
} from './auth-config';
import { AuthStateService } from './auth-state/auth-state.service';
import { CheckAuthService } from './auth-state/check-auth.service';
import { AutoLoginService } from './auto-login/auto-login.service';
import { CallbackService } from './callback/callback.service';
import { CodeFlowCallbackService } from './callback/code-flow-callback.service';
import { ImplicitFlowCallbackService } from './callback/implicit-flow-callback.service';
import { IntervalService } from './callback/interval.service';
import { PeriodicallyTokenCheckService } from './callback/periodically-token-check.service';
import { RefreshSessionRefreshTokenService } from './callback/refresh-session-refresh-token.service';
import { RefreshSessionService } from './callback/refresh-session.service';
import { AuthWellKnownDataService } from './config/auth-well-known/auth-well-known-data.service';
import { AuthWellKnownService } from './config/auth-well-known/auth-well-known.service';
import { ConfigurationService } from './config/config.service';
import { StsConfigLoader } from './config/loader/config-loader';
import { ConfigValidationService } from './config/validation/config-validation.service';
import { DOCUMENT } from './dom';
import { JwkExtractor } from './extractors/jwk.extractor';
import { CodeFlowCallbackHandlerService } from './flows/callback-handling/code-flow-callback-handler.service';
import { HistoryJwtKeysCallbackHandlerService } from './flows/callback-handling/history-jwt-keys-callback-handler.service';
import { ImplicitFlowCallbackHandlerService } from './flows/callback-handling/implicit-flow-callback-handler.service';
import { RefreshSessionCallbackHandlerService } from './flows/callback-handling/refresh-session-callback-handler.service';
import { RefreshTokenCallbackHandlerService } from './flows/callback-handling/refresh-token-callback-handler.service';
import { StateValidationCallbackHandlerService } from './flows/callback-handling/state-validation-callback-handler.service';
import { UserCallbackHandlerService } from './flows/callback-handling/user-callback-handler.service';
import { FlowsDataService } from './flows/flows-data.service';
import { FlowsService } from './flows/flows.service';
import { RandomService } from './flows/random/random.service';
import { ResetAuthDataService } from './flows/reset-auth-data.service';
import { SigninKeyDataService } from './flows/signin-key-data.service';
import { CheckSessionService } from './iframe/check-session.service';
import { IFrameService } from './iframe/existing-iframe.service';
import { RefreshSessionIframeService } from './iframe/refresh-session-iframe.service';
import { SilentRenewService } from './iframe/silent-renew.service';
import { ClosestMatchingRouteService } from './interceptor/closest-matching-route.service';
import { AbstractLoggerService } from './logging/abstract-logger.service';
import { ConsoleLoggerService } from './logging/console-logger.service';
import { LoggerService } from './logging/logger.service';
import { LoginService } from './login/login.service';
import { ParLoginService } from './login/par/par-login.service';
import { ParService } from './login/par/par.service';
import { PopUpLoginService } from './login/popup/popup-login.service';
import { PopUpService } from './login/popup/popup.service';
import { ResponseTypeValidationService } from './login/response-type-validation/response-type-validation.service';
import { StandardLoginService } from './login/standard/standard-login.service';
import { LogoffRevocationService } from './logoff-revoke/logoff-revocation.service';
import { OidcSecurityService } from './oidc.security.service';
import { PublicEventsService } from './public-events/public-events.service';
import { AbstractSecurityStorage } from './storage/abstract-security-storage';
import { BrowserStorageService } from './storage/browser-storage.service';
import { DefaultSessionStorageService } from './storage/default-sessionstorage.service';
import { StoragePersistenceService } from './storage/storage-persistence.service';
import { UserService } from './user-data/user.service';
import { CryptoService } from './utils/crypto/crypto.service';
import { EqualityService } from './utils/equality/equality.service';
import { FlowHelper } from './utils/flowHelper/flow-helper.service';
import {
PLATFORM_ID,
PlatformProvider,
} from './utils/platform-provider/platform.provider';
import { RedirectService } from './utils/redirect/redirect.service';
import { TokenHelperService } from './utils/tokenHelper/token-helper.service';
import { CurrentUrlService } from './utils/url/current-url.service';
import { UrlService } from './utils/url/url.service';
import { JwkWindowCryptoService } from './validation/jwk-window-crypto.service';
import { JwtWindowCryptoService } from './validation/jwt-window-crypto.service';
import { StateValidationService } from './validation/state-validation.service';
import { TokenValidationService } from './validation/token-validation.service';
/**
* A feature to be used with `provideAuth`.
@ -32,9 +97,17 @@ export function provideAuth(
export function _provideAuth(passedConfig: PassedInitialConfig): Provider[] {
return [
{
provide: DOCUMENT,
useFactory: () => document,
},
HttpClient,
{
provide: PLATFORM_ID,
useValue: 'browser',
},
// Make the PASSED_CONFIG available through injection
{ provide: PASSED_CONFIG, useValue: passedConfig },
// Create the loader: Either the one getting passed or a static one
passedConfig?.loader || {
provide: StsConfigLoader,
@ -46,5 +119,65 @@ export function _provideAuth(passedConfig: PassedInitialConfig): Provider[] {
useClass: DefaultSessionStorageService,
},
{ provide: AbstractLoggerService, useClass: ConsoleLoggerService },
StandardLoginService,
JwkWindowCryptoService,
ParLoginService,
AuthStateService,
RefreshTokenCallbackHandlerService,
JwkExtractor,
TokenHelperService,
RefreshSessionIframeService,
ImplicitFlowCallbackHandlerService,
CallbackService,
EqualityService,
FlowsDataService,
PopUpService,
FlowsService,
StoragePersistenceService,
ClosestMatchingRouteService,
CryptoService,
AutoLoginService,
RedirectService,
LoginService,
CurrentUrlService,
UserCallbackHandlerService,
IFrameService,
BrowserStorageService,
ParService,
LoggerService,
RandomService,
PopUpLoginService,
AuthWellKnownService,
RefreshSessionRefreshTokenService,
CodeFlowCallbackService,
StateValidationService,
FlowHelper,
ImplicitFlowCallbackService,
PublicEventsService,
CodeFlowCallbackHandlerService,
UrlService,
ConfigValidationService,
RefreshSessionService,
HttpBaseService,
AuthWellKnownDataService,
CheckAuthService,
SilentRenewService,
StateValidationCallbackHandlerService,
JwtWindowCryptoService,
ResetAuthDataService,
IntervalService,
LogoffRevocationService,
HistoryJwtKeysCallbackHandlerService,
PlatformProvider,
RefreshSessionCallbackHandlerService,
CheckSessionService,
ConfigurationService,
ResponseTypeValidationService,
DataService,
UserService,
PeriodicallyTokenCheckService,
SigninKeyDataService,
TokenValidationService,
OidcSecurityService,
];
}