@axa-fr/react-oidc 5.7.0-alpha0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +502 -0
- package/dist/FetchToken.d.ts +10 -0
- package/dist/FetchToken.d.ts.map +1 -0
- package/dist/FetchToken.js +107 -0
- package/dist/FetchToken.js.map +1 -0
- package/dist/OidcProvider.d.ts +25 -0
- package/dist/OidcProvider.d.ts.map +1 -0
- package/dist/OidcProvider.js +133 -0
- package/dist/OidcProvider.js.map +1 -0
- package/dist/OidcSecure.d.ts +10 -0
- package/dist/OidcSecure.d.ts.map +1 -0
- package/dist/OidcSecure.js +68 -0
- package/dist/OidcSecure.js.map +1 -0
- package/dist/OidcServiceWorker.js +272 -0
- package/dist/OidcTrustedDomains.js +6 -0
- package/dist/ReactOidc.d.ts +17 -0
- package/dist/ReactOidc.d.ts.map +1 -0
- package/dist/ReactOidc.js +106 -0
- package/dist/ReactOidc.js.map +1 -0
- package/dist/User.d.ts +15 -0
- package/dist/User.d.ts.map +1 -0
- package/dist/User.js +48 -0
- package/dist/User.js.map +1 -0
- package/dist/core/default-component/AuthenticateError.component.d.ts +4 -0
- package/dist/core/default-component/AuthenticateError.component.d.ts.map +1 -0
- package/dist/core/default-component/AuthenticateError.component.js +32 -0
- package/dist/core/default-component/AuthenticateError.component.js.map +1 -0
- package/dist/core/default-component/Authenticating.component.d.ts +4 -0
- package/dist/core/default-component/Authenticating.component.d.ts.map +1 -0
- package/dist/core/default-component/Authenticating.component.js +32 -0
- package/dist/core/default-component/Authenticating.component.js.map +1 -0
- package/dist/core/default-component/Callback.component.d.ts +5 -0
- package/dist/core/default-component/Callback.component.d.ts.map +1 -0
- package/dist/core/default-component/Callback.component.js +118 -0
- package/dist/core/default-component/Callback.component.js.map +1 -0
- package/dist/core/default-component/Loading.component.d.ts +4 -0
- package/dist/core/default-component/Loading.component.d.ts.map +1 -0
- package/dist/core/default-component/Loading.component.js +29 -0
- package/dist/core/default-component/Loading.component.js.map +1 -0
- package/dist/core/default-component/ServiceWorkerInstall.component.d.ts +4 -0
- package/dist/core/default-component/ServiceWorkerInstall.component.d.ts.map +1 -0
- package/dist/core/default-component/ServiceWorkerInstall.component.js +122 -0
- package/dist/core/default-component/ServiceWorkerInstall.component.js.map +1 -0
- package/dist/core/default-component/ServiceWorkerNotSupported.component.d.ts +4 -0
- package/dist/core/default-component/ServiceWorkerNotSupported.component.d.ts.map +1 -0
- package/dist/core/default-component/ServiceWorkerNotSupported.component.js +32 -0
- package/dist/core/default-component/ServiceWorkerNotSupported.component.js.map +1 -0
- package/dist/core/default-component/SessionLost.component.d.ts +4 -0
- package/dist/core/default-component/SessionLost.component.d.ts.map +1 -0
- package/dist/core/default-component/SessionLost.component.js +14 -0
- package/dist/core/default-component/SessionLost.component.js.map +1 -0
- package/dist/core/default-component/SilentCallback.component.d.ts +4 -0
- package/dist/core/default-component/SilentCallback.component.d.ts.map +1 -0
- package/dist/core/default-component/SilentCallback.component.js +96 -0
- package/dist/core/default-component/SilentCallback.component.js.map +1 -0
- package/dist/core/default-component/index.d.ts +7 -0
- package/dist/core/default-component/index.d.ts.map +1 -0
- package/dist/core/default-component/index.js +20 -0
- package/dist/core/default-component/index.js.map +1 -0
- package/dist/core/routes/OidcRoutes.d.ts +12 -0
- package/dist/core/routes/OidcRoutes.d.ts.map +1 -0
- package/dist/core/routes/OidcRoutes.js +71 -0
- package/dist/core/routes/OidcRoutes.js.map +1 -0
- package/dist/core/routes/index.d.ts +3 -0
- package/dist/core/routes/index.d.ts.map +1 -0
- package/dist/core/routes/index.js +9 -0
- package/dist/core/routes/index.js.map +1 -0
- package/dist/core/routes/route-utils.d.ts +2 -0
- package/dist/core/routes/route-utils.d.ts.map +1 -0
- package/dist/core/routes/route-utils.js +32 -0
- package/dist/core/routes/route-utils.js.map +1 -0
- package/dist/core/routes/withRouter.d.ts +19 -0
- package/dist/core/routes/withRouter.d.ts.map +1 -0
- package/dist/core/routes/withRouter.js +33 -0
- package/dist/core/routes/withRouter.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/vanilla/index.d.ts +2 -0
- package/dist/vanilla/index.d.ts.map +1 -0
- package/dist/vanilla/index.js +6 -0
- package/dist/vanilla/index.js.map +1 -0
- package/dist/vanilla/initSession.d.ts +11 -0
- package/dist/vanilla/initSession.d.ts.map +1 -0
- package/dist/vanilla/initSession.js +72 -0
- package/dist/vanilla/initSession.js.map +1 -0
- package/dist/vanilla/initWorker.d.ts +13 -0
- package/dist/vanilla/initWorker.d.ts.map +1 -0
- package/dist/vanilla/initWorker.js +211 -0
- package/dist/vanilla/initWorker.js.map +1 -0
- package/dist/vanilla/memoryStorageBackend.d.ts +10 -0
- package/dist/vanilla/memoryStorageBackend.d.ts.map +1 -0
- package/dist/vanilla/memoryStorageBackend.js +33 -0
- package/dist/vanilla/memoryStorageBackend.js.map +1 -0
- package/dist/vanilla/noHashQueryStringUtils.d.ts +5 -0
- package/dist/vanilla/noHashQueryStringUtils.d.ts.map +1 -0
- package/dist/vanilla/noHashQueryStringUtils.js +31 -0
- package/dist/vanilla/noHashQueryStringUtils.js.map +1 -0
- package/dist/vanilla/oidc.d.ts +77 -0
- package/dist/vanilla/oidc.d.ts.map +1 -0
- package/dist/vanilla/oidc.js +814 -0
- package/dist/vanilla/oidc.js.map +1 -0
- package/dist/vanilla/timer.d.ts +8 -0
- package/dist/vanilla/timer.d.ts.map +1 -0
- package/dist/vanilla/timer.js +135 -0
- package/dist/vanilla/timer.js.map +1 -0
- package/package.json +73 -0
- package/src/App.css +38 -0
- package/src/App.specold.tsx +46 -0
- package/src/App.tsx +61 -0
- package/src/FetchUser.tsx +53 -0
- package/src/Home.tsx +20 -0
- package/src/MultiAuth.tsx +114 -0
- package/src/Profile.tsx +77 -0
- package/src/configurations.ts +53 -0
- package/src/index.css +13 -0
- package/src/index.tsx +9 -0
- package/src/logo.svg +7 -0
- package/src/oidc/FetchToken.tsx +51 -0
- package/src/oidc/OidcProvider.tsx +165 -0
- package/src/oidc/OidcSecure.tsx +32 -0
- package/src/oidc/ReactOidc.tsx +112 -0
- package/src/oidc/User.ts +38 -0
- package/src/oidc/core/default-component/AuthenticateError.component.tsx +13 -0
- package/src/oidc/core/default-component/Authenticating.component.tsx +13 -0
- package/src/oidc/core/default-component/Callback.component.tsx +49 -0
- package/src/oidc/core/default-component/Loading.component.tsx +10 -0
- package/src/oidc/core/default-component/ServiceWorkerInstall.component.tsx +51 -0
- package/src/oidc/core/default-component/ServiceWorkerNotSupported.component.tsx +13 -0
- package/src/oidc/core/default-component/SessionLost.component.tsx +14 -0
- package/src/oidc/core/default-component/SilentCallback.component.tsx +31 -0
- package/src/oidc/core/default-component/index.ts +6 -0
- package/src/oidc/core/routes/OidcRoutes.spec.tsx +16 -0
- package/src/oidc/core/routes/OidcRoutes.tsx +69 -0
- package/src/oidc/core/routes/__snapshots__/OidcRoutes.spec.tsx.snap +7 -0
- package/src/oidc/core/routes/index.ts +2 -0
- package/src/oidc/core/routes/route-utils.spec.ts +9 -0
- package/src/oidc/core/routes/route-utils.ts +34 -0
- package/src/oidc/core/routes/withRouter.spec.tsx +48 -0
- package/src/oidc/core/routes/withRouter.tsx +60 -0
- package/src/oidc/index.ts +5 -0
- package/src/oidc/vanilla/OidcServiceWorker.js +272 -0
- package/src/oidc/vanilla/OidcTrustedDomains.js +6 -0
- package/src/oidc/vanilla/index.ts +1 -0
- package/src/oidc/vanilla/initSession.ts +36 -0
- package/src/oidc/vanilla/initWorker.ts +153 -0
- package/src/oidc/vanilla/memoryStorageBackend.ts +33 -0
- package/src/oidc/vanilla/noHashQueryStringUtils.ts +7 -0
- package/src/oidc/vanilla/oidc.ts +600 -0
- package/src/oidc/vanilla/timer.ts +157 -0
- package/src/override/AuthenticateError.component.tsx +14 -0
- package/src/override/Authenticating.component.tsx +14 -0
- package/src/override/Callback.component.tsx +13 -0
- package/src/override/Loading.component.tsx +13 -0
- package/src/override/ServiceWorkerNotSupported.component.tsx +15 -0
- package/src/override/SessionLost.component.tsx +21 -0
- package/src/override/style.ts +10 -0
- package/src/setupTests.js +5 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, {useEffect, useState, ComponentType} from 'react';
|
|
2
|
+
import AuthenticatingError from "./AuthenticateError.component";
|
|
3
|
+
import Oidc from "../../vanilla/oidc";
|
|
4
|
+
import Authenticating from "./Authenticating.component";
|
|
5
|
+
|
|
6
|
+
const ServiceWorkerInstall: ComponentType<any> = ({callBackError, authenticating, configurationName }) => {
|
|
7
|
+
const getOidc = Oidc.get;
|
|
8
|
+
const [error, setError] = useState(false);
|
|
9
|
+
const [isLoading, setLoading] = useState(true);
|
|
10
|
+
|
|
11
|
+
const CallbackErrorComponent = callBackError || AuthenticatingError;
|
|
12
|
+
const CallbackSuccessComponent = authenticating || Authenticating;
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
let isMounted = true;
|
|
16
|
+
const playCallbackAsync = async () => {
|
|
17
|
+
try {
|
|
18
|
+
const params = new Proxy(new URLSearchParams(window.location.search), {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
get: (searchParams, prop) => searchParams.get(prop),
|
|
21
|
+
});
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
await getOidc(configurationName).loginAsync(decodeURIComponent(params.callbackPath), false);
|
|
24
|
+
if(isMounted) {
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if(isMounted) {
|
|
29
|
+
setError(true);
|
|
30
|
+
setLoading(false);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
playCallbackAsync();
|
|
35
|
+
return () => {
|
|
36
|
+
isMounted = false;
|
|
37
|
+
};
|
|
38
|
+
},[]);
|
|
39
|
+
|
|
40
|
+
if(isLoading){
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if(error){
|
|
45
|
+
return <CallbackErrorComponent configurationName={configurationName} />
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <CallbackSuccessComponent configurationName={configurationName} />;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default ServiceWorkerInstall;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {ComponentType} from "react";
|
|
3
|
+
|
|
4
|
+
const ServiceWorkerNotSupported : ComponentType<any> = () => (
|
|
5
|
+
<div className="oidc-serviceworker">
|
|
6
|
+
<div className="oidc-serviceworker__container">
|
|
7
|
+
<h1 className="oidc-serviceworker__title">Unable to authenticate on this browser</h1>
|
|
8
|
+
<p className="oidc-serviceworker__content">Your browser is not secure enough to make authentication work. Try updating your browser or use a newer browser.</p>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default ServiceWorkerNotSupported;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React, {ComponentType} from 'react';
|
|
2
|
+
|
|
3
|
+
export const SessionLost: ComponentType<any> = () => (
|
|
4
|
+
<div className="oidc-session-lost">
|
|
5
|
+
<div className="oidc-session-lost__container">
|
|
6
|
+
<h1 className="oidc-session-lost__title">Session timed out</h1>
|
|
7
|
+
<p className="oidc-session-lost__content">
|
|
8
|
+
Your session has expired. Please re-authenticate.
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export default SessionLost;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, {useEffect, ComponentType} from 'react';
|
|
2
|
+
import Oidc from "../../vanilla/oidc";
|
|
3
|
+
import {OidcSecure} from "../../OidcSecure";
|
|
4
|
+
|
|
5
|
+
const CallBack = ({configurationName}) =>{
|
|
6
|
+
const getOidc = Oidc.get;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let isMounted = true;
|
|
9
|
+
const playCallbackAsync = async () => {
|
|
10
|
+
if(isMounted) {
|
|
11
|
+
const oidc = getOidc(configurationName);
|
|
12
|
+
oidc.silentSigninCallbackFromIFrame();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
playCallbackAsync();
|
|
16
|
+
|
|
17
|
+
return () => {
|
|
18
|
+
isMounted = false;
|
|
19
|
+
};
|
|
20
|
+
},[]);
|
|
21
|
+
|
|
22
|
+
return <></>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CallbackManager: ComponentType<any> = ({configurationName }) => {
|
|
26
|
+
return <OidcSecure configurationName={configurationName}>
|
|
27
|
+
<CallBack configurationName={configurationName}/>
|
|
28
|
+
</OidcSecure>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default CallbackManager;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as Authenticating } from './Authenticating.component';
|
|
2
|
+
export { default as Callback, CallBackSuccess } from './Callback.component';
|
|
3
|
+
export { default as SessionLost } from './SessionLost.component';
|
|
4
|
+
export { default as Loading } from './Loading.component';
|
|
5
|
+
export { default as AuthenticateError } from './AuthenticateError.component';
|
|
6
|
+
export { default as ServiceWorkerNotSupported } from './ServiceWorkerNotSupported.component';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import OidcRoutes from './OidcRoutes';
|
|
3
|
+
import { FC } from 'react';
|
|
4
|
+
import {render} from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
describe('Authenticating test suite', () => {
|
|
7
|
+
it('renders correctly', () => {
|
|
8
|
+
const props = {
|
|
9
|
+
children: 'http://url.com',
|
|
10
|
+
callbackComponent: () => <div>tcallback component</div>,
|
|
11
|
+
redirect_uri: 'http://example.com:3000/authentication/callback',
|
|
12
|
+
};
|
|
13
|
+
const { asFragment } = render(<OidcRoutes {...props} />);
|
|
14
|
+
expect(asFragment()).toMatchSnapshot();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, { ComponentType, FC, PropsWithChildren, useEffect, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { getPath } from './route-utils';
|
|
4
|
+
import CallbackComponent from '../default-component/Callback.component';
|
|
5
|
+
import SilentCallbackComponent from "../default-component/SilentCallback.component";
|
|
6
|
+
import ServiceWorkerInstall from "../default-component/ServiceWorkerInstall.component";
|
|
7
|
+
|
|
8
|
+
const propTypes = {
|
|
9
|
+
callbackComponent: PropTypes.elementType,
|
|
10
|
+
redirect_uri: PropTypes.string.isRequired,
|
|
11
|
+
children: PropTypes.node,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const defaultProps: Partial<OidcRoutesProps> = {
|
|
15
|
+
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type OidcRoutesProps = {
|
|
19
|
+
callbackSuccessComponent?: ComponentType;
|
|
20
|
+
callbackErrorComponent?: ComponentType;
|
|
21
|
+
authenticatingComponent?: ComponentType;
|
|
22
|
+
configurationName:string;
|
|
23
|
+
redirect_uri: string;
|
|
24
|
+
silent_redirect_uri?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const OidcRoutes: FC<PropsWithChildren<OidcRoutesProps>> = ({
|
|
28
|
+
callbackErrorComponent,
|
|
29
|
+
callbackSuccessComponent,
|
|
30
|
+
authenticatingComponent,
|
|
31
|
+
redirect_uri,
|
|
32
|
+
silent_redirect_uri,
|
|
33
|
+
children, configurationName
|
|
34
|
+
}) => {
|
|
35
|
+
// This exist because in next.js window outside useEffect is null
|
|
36
|
+
const pathname = window ? window.location.pathname : '';
|
|
37
|
+
|
|
38
|
+
const [path, setPath] = useState(pathname);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const setNewPath = () => setPath(window.location.pathname);
|
|
42
|
+
setNewPath();
|
|
43
|
+
window.addEventListener('popstate', setNewPath, false);
|
|
44
|
+
return () => window.removeEventListener('popstate', setNewPath, false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const callbackPath = getPath(redirect_uri);
|
|
48
|
+
|
|
49
|
+
if(silent_redirect_uri){
|
|
50
|
+
if(path === getPath(silent_redirect_uri)){
|
|
51
|
+
return <SilentCallbackComponent configurationName={configurationName} />
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (path) {
|
|
56
|
+
case callbackPath:
|
|
57
|
+
return <CallbackComponent callBackError={callbackErrorComponent} callBackSuccess={callbackSuccessComponent} configurationName={configurationName} />;
|
|
58
|
+
case callbackPath +"/service-worker-install" :
|
|
59
|
+
return <ServiceWorkerInstall callBackError={callbackErrorComponent} authenticating={authenticatingComponent} configurationName={configurationName} />;
|
|
60
|
+
default:
|
|
61
|
+
return <>{children}</>;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
OidcRoutes.propTypes = propTypes;
|
|
67
|
+
OidcRoutes.defaultProps = defaultProps;
|
|
68
|
+
|
|
69
|
+
export default React.memo(OidcRoutes);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getPath } from './route-utils';
|
|
2
|
+
|
|
3
|
+
it('getPath should return the full path of an url', () => {
|
|
4
|
+
const path1 = getPath('http://example.com/pathname');
|
|
5
|
+
const path2 = getPath('http://example.com:3000/pathname/?search=test#hash');
|
|
6
|
+
|
|
7
|
+
expect(path1).toEqual('/pathname');
|
|
8
|
+
expect(path2).toEqual('/pathname/?search=test#hash');
|
|
9
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const getLocation = (href: string) => {
|
|
2
|
+
const match = href.match(
|
|
3
|
+
// eslint-disable-next-line no-useless-escape
|
|
4
|
+
/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
|
|
5
|
+
);
|
|
6
|
+
return (
|
|
7
|
+
match && {
|
|
8
|
+
href,
|
|
9
|
+
protocol: match[1],
|
|
10
|
+
host: match[2],
|
|
11
|
+
hostname: match[3],
|
|
12
|
+
port: match[4],
|
|
13
|
+
path: match[5],
|
|
14
|
+
search: match[6],
|
|
15
|
+
hash: match[7],
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getPath = (href: string) => {
|
|
21
|
+
const location = getLocation(href);
|
|
22
|
+
let { path } = location;
|
|
23
|
+
const { search, hash } = location;
|
|
24
|
+
|
|
25
|
+
if (search) {
|
|
26
|
+
path += search;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (hash) {
|
|
30
|
+
path += hash;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return path;
|
|
34
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
3
|
+
import { CreateEvent, WindowInternal } from './withRouter';
|
|
4
|
+
|
|
5
|
+
describe('WithRouter test Suite', () => {
|
|
6
|
+
const generateKeyMock = () => '123ABC';
|
|
7
|
+
const paramsMock = { bubbles: false, cancelable: false, detail: 'detail' };
|
|
8
|
+
beforeEach(() => {});
|
|
9
|
+
it('should CreateEvent return correct Event if not on IE', () => {
|
|
10
|
+
const windowMock = {
|
|
11
|
+
CustomEvent: jest.fn().mockImplementation((event, params) => {
|
|
12
|
+
return { event, params };
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
const documentMock = {} as Document;
|
|
16
|
+
const res = CreateEvent((windowMock as unknown) as WindowInternal, documentMock)(
|
|
17
|
+
'event test',
|
|
18
|
+
paramsMock
|
|
19
|
+
);
|
|
20
|
+
expect(res).toEqual({
|
|
21
|
+
event: 'event test',
|
|
22
|
+
params: { bubbles: false, cancelable: false, detail: 'detail' },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should createEvent create a polyfill when the default func is undefined', () => {
|
|
27
|
+
const windowMock = {
|
|
28
|
+
Event: {
|
|
29
|
+
prototype: 'protoMock',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const evtMock = {
|
|
33
|
+
initCustomEvent: jest.fn(),
|
|
34
|
+
};
|
|
35
|
+
const documentMock = {
|
|
36
|
+
createEvent: jest.fn(() => evtMock),
|
|
37
|
+
};
|
|
38
|
+
const typedDocumentMock = (documentMock as unknown) as Document;
|
|
39
|
+
const res = CreateEvent((windowMock as unknown) as WindowInternal, typedDocumentMock)(
|
|
40
|
+
'event test',
|
|
41
|
+
paramsMock
|
|
42
|
+
);
|
|
43
|
+
expect(res).toEqual({ ...evtMock });
|
|
44
|
+
expect(documentMock.createEvent).toHaveBeenCalledWith('CustomEvent');
|
|
45
|
+
expect(evtMock.initCustomEvent).toHaveBeenCalledWith('event test', false, false, 'detail');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
const generateKey = () =>
|
|
4
|
+
Math.random()
|
|
5
|
+
.toString(36)
|
|
6
|
+
.substr(2, 6);
|
|
7
|
+
|
|
8
|
+
// Exported only for test
|
|
9
|
+
export type WindowInternal = Window & {
|
|
10
|
+
CustomEvent?: new <T>(typeArg: string, eventInitDict?: CustomEventInit<T>) => CustomEvent<T>;
|
|
11
|
+
Event: typeof Event;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type IPrototype = {
|
|
15
|
+
prototype: any;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type InitCustomEventParams<T = any> = {
|
|
19
|
+
bubbles: boolean;
|
|
20
|
+
cancelable: boolean;
|
|
21
|
+
detail: T;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// IE Polyfill for CustomEvent
|
|
25
|
+
export const CreateEvent = (windowInternal: WindowInternal, documentInternal: Document) => (
|
|
26
|
+
event: string,
|
|
27
|
+
params: InitCustomEventParams
|
|
28
|
+
): CustomEvent => {
|
|
29
|
+
if (typeof windowInternal.CustomEvent === 'function') {
|
|
30
|
+
return new windowInternal.CustomEvent(event, params);
|
|
31
|
+
}
|
|
32
|
+
const paramsToFunction = params || { bubbles: false, cancelable: false, detail: undefined };
|
|
33
|
+
const evt: CustomEvent = documentInternal.createEvent('CustomEvent');
|
|
34
|
+
evt.initCustomEvent(event, paramsToFunction.bubbles, paramsToFunction.cancelable, paramsToFunction.detail);
|
|
35
|
+
(evt as CustomEvent & IPrototype).prototype = windowInternal.Event.prototype;
|
|
36
|
+
return evt;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type WindowHistoryState = typeof window.history.state;
|
|
40
|
+
|
|
41
|
+
export interface ReactOidcHistory {
|
|
42
|
+
replaceState: (url?: string | null, stateHistory?: WindowHistoryState) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const getHistory = (
|
|
46
|
+
windowInternal: WindowInternal,
|
|
47
|
+
CreateEventInternal: (event: string, params?: InitCustomEventParams) => CustomEvent,
|
|
48
|
+
generateKeyInternal: typeof generateKey
|
|
49
|
+
) => {
|
|
50
|
+
return {
|
|
51
|
+
replaceState: (url?: string | null, stateHistory?: WindowHistoryState): void => {
|
|
52
|
+
const key = generateKeyInternal();
|
|
53
|
+
const state = stateHistory || windowInternal.history.state;
|
|
54
|
+
windowInternal.history.replaceState({ key, state }, null, url);
|
|
55
|
+
windowInternal.dispatchEvent(CreateEventInternal('popstate'));
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const getCustomHistory = () => getHistory(window, CreateEvent(window, document), generateKey);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { withOidcSecure, OidcSecure } from "./OidcSecure";
|
|
2
|
+
export { useOidcUser, OidcUserStatus} from "./User";
|
|
3
|
+
export { useOidc, useOidcAccessToken, useOidcIdToken } from "./ReactOidc";
|
|
4
|
+
export { withOidcFetch, useOidcFetch } from "./FetchToken";
|
|
5
|
+
export { OidcProvider } from "./OidcProvider";
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
this.importScripts('OidcTrustedDomains.js');
|
|
2
|
+
|
|
3
|
+
const id = Math.round(new Date().getTime() / 1000).toString();
|
|
4
|
+
|
|
5
|
+
const keepAliveJsonFilename = "OidcKeepAliveServiceWorker.json";
|
|
6
|
+
const handleInstall = (event) => {
|
|
7
|
+
console.log('[OidcServiceWorker] service worker installed ' + id);
|
|
8
|
+
self.skipWaiting();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const handleActivate = () => {
|
|
12
|
+
console.log('[OidcServiceWorker] service worker activated ' + id);
|
|
13
|
+
self.clients.claim();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let currentLoginCallbackConfigurationName = null;
|
|
17
|
+
let database = {
|
|
18
|
+
default: {
|
|
19
|
+
configurationName: "default",
|
|
20
|
+
tokens: null,
|
|
21
|
+
items:[],
|
|
22
|
+
oidcServerConfiguration: null
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const countLetter = (str, find)=> {
|
|
27
|
+
return (str.split(find)).length - 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function extractAccessTokenPayload(accessToken) {
|
|
31
|
+
try{
|
|
32
|
+
if (!accessToken) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if(countLetter(accessToken,'.') === 2) {
|
|
36
|
+
return JSON.parse(atob(accessToken.split('.')[1]));
|
|
37
|
+
} else {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.warn(e);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hideTokens(currentDatabaseElement) {
|
|
47
|
+
const configurationName = currentDatabaseElement.configurationName;
|
|
48
|
+
return (response) => {
|
|
49
|
+
return response.json().then(tokens => {
|
|
50
|
+
currentDatabaseElement.tokens = tokens;
|
|
51
|
+
const secureTokens = {
|
|
52
|
+
...tokens,
|
|
53
|
+
access_token: ACCESS_TOKEN +"_" + configurationName,
|
|
54
|
+
refresh_token: REFRESH_TOKEN + "_" + configurationName,
|
|
55
|
+
};
|
|
56
|
+
const body = JSON.stringify(secureTokens)
|
|
57
|
+
return new Response(body, response);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const getCurrentDatabasesTokenEndpoint = (database, url) => {
|
|
63
|
+
const databases = [];
|
|
64
|
+
for (const [key, value] of Object.entries(database)) {
|
|
65
|
+
if(value && value.oidcServerConfiguration !=null && url.startsWith(value.oidcServerConfiguration.tokenEndpoint)){
|
|
66
|
+
databases.push(value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return databases;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const getCurrentDatabaseDomain = (database, url) => {
|
|
73
|
+
for (const [key, currentDatabase] of Object.entries(database)) {
|
|
74
|
+
|
|
75
|
+
const oidcServerConfiguration = currentDatabase.oidcServerConfiguration;
|
|
76
|
+
const domainsToSendTokens = oidcServerConfiguration != null ? [
|
|
77
|
+
oidcServerConfiguration.userInfoEndpoint, ...trustedDomains[key]
|
|
78
|
+
] : [...trustedDomains[key]];
|
|
79
|
+
|
|
80
|
+
let hasToSendToken = false;
|
|
81
|
+
for(let i=0;i<domainsToSendTokens.length;i++) {
|
|
82
|
+
const domain = domainsToSendTokens[i];
|
|
83
|
+
if (url.startsWith(domain)) {
|
|
84
|
+
hasToSendToken = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if(hasToSendToken){
|
|
90
|
+
if(!currentDatabase.tokens) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return currentDatabase;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const serializeHeaders = (headers) => {
|
|
101
|
+
let headersObj = {};
|
|
102
|
+
for (let key of headers.keys()) {
|
|
103
|
+
headersObj[key] = headers.get(key);
|
|
104
|
+
}
|
|
105
|
+
return headersObj;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const REFRESH_TOKEN = 'REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER';
|
|
109
|
+
const ACCESS_TOKEN = 'ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER';
|
|
110
|
+
|
|
111
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
112
|
+
|
|
113
|
+
const keepAliveAsync = async (event) => {
|
|
114
|
+
const originalRequest = event.request;
|
|
115
|
+
const isFromVanilla = originalRequest.headers.has('oidc-vanilla');
|
|
116
|
+
if(!isFromVanilla) {
|
|
117
|
+
await sleep(15000);
|
|
118
|
+
}
|
|
119
|
+
const init = {"status": 200, "statusText": 'oidc-service-worker'};
|
|
120
|
+
return new Response('{}', init);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handleFetch = async (event) => {
|
|
124
|
+
const originalRequest = event.request;
|
|
125
|
+
|
|
126
|
+
if(originalRequest.url.includes(keepAliveJsonFilename) ){
|
|
127
|
+
event.respondWith(keepAliveAsync(event));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const currentDatabaseForRequestAccessToken = getCurrentDatabaseDomain(database, originalRequest.url);
|
|
132
|
+
if(currentDatabaseForRequestAccessToken && currentDatabaseForRequestAccessToken.tokens) {
|
|
133
|
+
const newRequest = new Request(originalRequest, {
|
|
134
|
+
headers: {
|
|
135
|
+
...serializeHeaders(originalRequest.headers),
|
|
136
|
+
authorization: "Bearer " + currentDatabaseForRequestAccessToken.tokens.access_token
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
event.waitUntil(event.respondWith(fetch(newRequest)));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if(event.request.method !== "POST"){
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let currentDatabase = null;
|
|
146
|
+
const currentDatabases = getCurrentDatabasesTokenEndpoint(database, originalRequest.url);
|
|
147
|
+
const numberDatabase = currentDatabases.length;
|
|
148
|
+
if(numberDatabase > 0) {
|
|
149
|
+
const maPromesse = new Promise((resolve, reject) => {
|
|
150
|
+
const response = originalRequest.clone().text().then(actualBody => {
|
|
151
|
+
if(actualBody.includes(REFRESH_TOKEN)) {
|
|
152
|
+
let newBody = actualBody;
|
|
153
|
+
for(let i= 0;i<numberDatabase;i++){
|
|
154
|
+
const currentDb = currentDatabases[i];
|
|
155
|
+
const key = REFRESH_TOKEN + '_'+ currentDb.configurationName;
|
|
156
|
+
if(currentDb && currentDb.tokens != null && actualBody.includes(key)) {
|
|
157
|
+
newBody = newBody.replace(key, encodeURIComponent(currentDb.tokens.refresh_token));
|
|
158
|
+
currentDatabase = currentDb;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return fetch(originalRequest, {
|
|
163
|
+
body: newBody,
|
|
164
|
+
method: originalRequest.method,
|
|
165
|
+
headers: {
|
|
166
|
+
...serializeHeaders(originalRequest.headers),
|
|
167
|
+
},
|
|
168
|
+
mode: originalRequest.mode,
|
|
169
|
+
cache: originalRequest.cache,
|
|
170
|
+
redirect: originalRequest.redirect,
|
|
171
|
+
referrer: originalRequest.referrer,
|
|
172
|
+
credentials: originalRequest.credentials,
|
|
173
|
+
integrity: originalRequest.integrity
|
|
174
|
+
}).then(hideTokens(currentDatabase));
|
|
175
|
+
} else if(currentLoginCallbackConfigurationName){
|
|
176
|
+
currentDatabase = database[currentLoginCallbackConfigurationName];
|
|
177
|
+
currentLoginCallbackConfigurationName=null;
|
|
178
|
+
return fetch(originalRequest,{
|
|
179
|
+
body: actualBody,
|
|
180
|
+
method: originalRequest.method,
|
|
181
|
+
headers: {
|
|
182
|
+
...serializeHeaders(originalRequest.headers),
|
|
183
|
+
},
|
|
184
|
+
mode: originalRequest.mode,
|
|
185
|
+
cache: originalRequest.cache,
|
|
186
|
+
redirect: originalRequest.redirect,
|
|
187
|
+
referrer: originalRequest.referrer,
|
|
188
|
+
credentials: originalRequest.credentials,
|
|
189
|
+
integrity: originalRequest.integrity
|
|
190
|
+
}).then(hideTokens(currentDatabase));
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
response.then(r => {
|
|
194
|
+
if(r !== undefined){
|
|
195
|
+
resolve(r);
|
|
196
|
+
}
|
|
197
|
+
}).catch(err => {
|
|
198
|
+
if(err !== undefined) {
|
|
199
|
+
reject(err);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
event.waitUntil(event.respondWith(maPromesse));
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
self.addEventListener('install', handleInstall);
|
|
208
|
+
self.addEventListener('activate', handleActivate);
|
|
209
|
+
self.addEventListener('fetch', handleFetch);
|
|
210
|
+
|
|
211
|
+
addEventListener('message', event => {
|
|
212
|
+
const port = event.ports[0];
|
|
213
|
+
const data = event.data;
|
|
214
|
+
const configurationName = data.configurationName;
|
|
215
|
+
let currentDatabase = database[configurationName];
|
|
216
|
+
|
|
217
|
+
if(!currentDatabase){
|
|
218
|
+
database[configurationName] = {
|
|
219
|
+
tokens: null,
|
|
220
|
+
items:[],
|
|
221
|
+
oidcServerConfiguration: null,
|
|
222
|
+
configurationName: configurationName,
|
|
223
|
+
};
|
|
224
|
+
currentDatabase = database[configurationName];
|
|
225
|
+
if(!trustedDomains[configurationName]) {
|
|
226
|
+
trustedDomains[configurationName] = [];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
switch (data.type){
|
|
230
|
+
case "loadItems":
|
|
231
|
+
port.postMessage(database[configurationName].items);
|
|
232
|
+
return;
|
|
233
|
+
case "clear":
|
|
234
|
+
currentDatabase.tokens = null;
|
|
235
|
+
currentDatabase.items = null;
|
|
236
|
+
port.postMessage({configurationName});
|
|
237
|
+
return;
|
|
238
|
+
case "init":
|
|
239
|
+
currentDatabase.oidcServerConfiguration = data.data.oidcServerConfiguration;
|
|
240
|
+
const where = data.data.where;
|
|
241
|
+
if(where === "loginCallbackAsync" || where === "tryKeepExistingSessionAsync") {
|
|
242
|
+
currentLoginCallbackConfigurationName = configurationName;
|
|
243
|
+
} else{
|
|
244
|
+
currentLoginCallbackConfigurationName = null;
|
|
245
|
+
}
|
|
246
|
+
if(!currentDatabase.tokens){
|
|
247
|
+
port.postMessage({
|
|
248
|
+
tokens:null,
|
|
249
|
+
configurationName});
|
|
250
|
+
} else {
|
|
251
|
+
port.postMessage({
|
|
252
|
+
tokens: {
|
|
253
|
+
...currentDatabase.tokens,
|
|
254
|
+
refresh_token: REFRESH_TOKEN + "_" + configurationName,
|
|
255
|
+
access_token: ACCESS_TOKEN + "_" + configurationName
|
|
256
|
+
},
|
|
257
|
+
configurationName
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
|
|
262
|
+
case "getAccessTokenPayload":
|
|
263
|
+
const accessTokenPayload = extractAccessTokenPayload(currentDatabase.tokens.access_token);
|
|
264
|
+
port.postMessage({configurationName, accessTokenPayload});
|
|
265
|
+
return;
|
|
266
|
+
default:
|
|
267
|
+
currentDatabase.items = data.data;
|
|
268
|
+
port.postMessage({configurationName});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Oidc } from './oidc';
|