@axa-fr/react-oidc 6.6.0 → 6.6.2-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/package.json +2 -1
- package/src/App.css +38 -0
- package/src/App.specold.tsx +46 -0
- package/src/App.tsx +103 -0
- package/src/FetchUser.tsx +53 -0
- package/src/Home.tsx +24 -0
- package/src/MultiAuth.tsx +129 -0
- package/src/Profile.tsx +77 -0
- package/src/configurations.ts +70 -0
- package/src/index.css +13 -0
- package/src/index.tsx +9 -0
- package/src/logo.svg +7 -0
- package/src/oidc/FetchToken.tsx +61 -0
- package/src/oidc/OidcProvider.tsx +206 -0
- package/src/oidc/OidcSecure.tsx +37 -0
- package/src/oidc/ReactOidc.tsx +139 -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 +46 -0
- package/src/oidc/core/default-component/Loading.component.tsx +10 -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 +22 -0
- package/src/oidc/core/default-component/SilentLogin.component.tsx +35 -0
- package/src/oidc/core/default-component/index.ts +6 -0
- package/src/oidc/core/routes/OidcRoutes.spec.tsx +15 -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/withRouter.spec.tsx +48 -0
- package/src/oidc/core/routes/withRouter.tsx +64 -0
- package/src/oidc/index.ts +5 -0
- package/src/oidc/vanilla/OidcServiceWorker.js +442 -0
- package/src/oidc/vanilla/OidcTrustedDomains.js +16 -0
- package/src/oidc/vanilla/checkSessionIFrame.ts +82 -0
- package/src/oidc/vanilla/index.ts +1 -0
- package/src/oidc/vanilla/initSession.ts +67 -0
- package/src/oidc/vanilla/initWorker.ts +165 -0
- package/src/oidc/vanilla/memoryStorageBackend.ts +33 -0
- package/src/oidc/vanilla/noHashQueryStringUtils.ts +33 -0
- package/src/oidc/vanilla/oidc.ts +1230 -0
- package/src/oidc/vanilla/parseTokens.ts +142 -0
- package/src/oidc/vanilla/route-utils.spec.ts +15 -0
- package/src/oidc/vanilla/route-utils.ts +76 -0
- package/src/oidc/vanilla/timer.ts +165 -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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const DefaultInterval = 2000;
|
|
2
|
+
|
|
3
|
+
const Log = console;
|
|
4
|
+
|
|
5
|
+
export class CheckSessionIFrame {
|
|
6
|
+
private readonly _client_id: any;
|
|
7
|
+
private readonly _callback: any;
|
|
8
|
+
private _url: any;
|
|
9
|
+
private readonly _interval: number;
|
|
10
|
+
private readonly _stopOnError: boolean;
|
|
11
|
+
private readonly _frame_origin: string;
|
|
12
|
+
private readonly _frame: HTMLIFrameElement;
|
|
13
|
+
private _boundMessageEvent: any;
|
|
14
|
+
private _timer: number;
|
|
15
|
+
constructor(callback, client_id, url, interval=DefaultInterval, stopOnError = true) {
|
|
16
|
+
this._callback = callback;
|
|
17
|
+
this._client_id = client_id;
|
|
18
|
+
this._url = url;
|
|
19
|
+
this._interval = interval || DefaultInterval;
|
|
20
|
+
this._stopOnError = stopOnError;
|
|
21
|
+
const idx = url.indexOf("/", url.indexOf("//") + 2);
|
|
22
|
+
this._frame_origin = url.substr(0, idx);
|
|
23
|
+
this._frame = window.document.createElement("iframe");
|
|
24
|
+
this._frame.style.visibility = "hidden";
|
|
25
|
+
this._frame.style.position = "absolute";
|
|
26
|
+
this._frame.style.display = "none";
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
this._frame.width = 0;
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
this._frame.height = 0;
|
|
31
|
+
|
|
32
|
+
this._frame.src = url;
|
|
33
|
+
}
|
|
34
|
+
load() {
|
|
35
|
+
return new Promise<void>((resolve) => {
|
|
36
|
+
this._frame.onload = () => {
|
|
37
|
+
resolve();
|
|
38
|
+
}
|
|
39
|
+
window.document.body.appendChild(this._frame);
|
|
40
|
+
this._boundMessageEvent = this._message.bind(this);
|
|
41
|
+
window.addEventListener("message", this._boundMessageEvent, false);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
_message(e) {
|
|
45
|
+
if (e.origin === this._frame_origin &&
|
|
46
|
+
e.source === this._frame.contentWindow
|
|
47
|
+
) {
|
|
48
|
+
if (e.data === "error") {
|
|
49
|
+
Log.error("CheckSessionIFrame: error message from check session op iframe");
|
|
50
|
+
if (this._stopOnError) {
|
|
51
|
+
this.stop();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (e.data === "changed") {
|
|
55
|
+
Log.debug(e)
|
|
56
|
+
Log.debug("CheckSessionIFrame: changed message from check session op iframe");
|
|
57
|
+
this.stop();
|
|
58
|
+
this._callback();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
Log.debug("CheckSessionIFrame: " + e.data + " message from check session op iframe");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
start(session_state) {
|
|
66
|
+
Log.debug("CheckSessionIFrame.start :" + session_state);
|
|
67
|
+
this.stop();
|
|
68
|
+
let send = () => {
|
|
69
|
+
this._frame.contentWindow.postMessage(this._client_id + " " + session_state, this._frame_origin);
|
|
70
|
+
};
|
|
71
|
+
send();
|
|
72
|
+
this._timer = window.setInterval(send, this._interval);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
stop() {
|
|
76
|
+
if (this._timer) {
|
|
77
|
+
Log.debug("CheckSessionIFrame.stop");
|
|
78
|
+
window.clearInterval(this._timer);
|
|
79
|
+
this._timer = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Oidc } from './oidc';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const initSession = (configurationName, redirectUri, storage=sessionStorage) => {
|
|
2
|
+
|
|
3
|
+
const saveItemsAsync =(items) =>{
|
|
4
|
+
storage[`oidc_items.${configurationName}:${redirectUri}`] = JSON.stringify(items);
|
|
5
|
+
return Promise.resolve();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const loadItemsAsync=() =>{
|
|
9
|
+
return Promise.resolve(JSON.parse(storage[`oidc_items.${configurationName}:${redirectUri}`]));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const clearAsync=(status) =>{
|
|
13
|
+
storage[`oidc.${configurationName}:${redirectUri}`] = JSON.stringify({tokens:null, status});
|
|
14
|
+
return Promise.resolve();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const initAsync=async () => {
|
|
18
|
+
if(!storage[`oidc.${configurationName}:${redirectUri}`]){
|
|
19
|
+
storage[`oidc.${configurationName}:${redirectUri}`] = JSON.stringify({tokens:null, status:null});
|
|
20
|
+
return {tokens:null, status:null};
|
|
21
|
+
}
|
|
22
|
+
const data = JSON.parse(storage[`oidc.${configurationName}:${redirectUri}`]);
|
|
23
|
+
return Promise.resolve({ tokens : data.tokens, status: data.status });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const setTokens = (tokens) => {
|
|
27
|
+
storage[`oidc.${configurationName}:${redirectUri}`] = JSON.stringify({tokens});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const setSessionState = (sessionState) => {
|
|
31
|
+
storage[`oidc.session_state.${configurationName}:${redirectUri}`] = sessionState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getSessionState= () =>{
|
|
35
|
+
return storage[`oidc.session_state.${configurationName}:${redirectUri}`];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const setNonceAsync = (nonce) => {
|
|
39
|
+
localStorage[`oidc.nonce.${configurationName}:${redirectUri}`] = nonce.nonce;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const getNonceAsync= async () => {
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
const result = {nonce: localStorage[`oidc.nonce.${configurationName}:${redirectUri}`]};
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const getTokens = () => {
|
|
49
|
+
if(!storage[`oidc.${configurationName}:${redirectUri}`]){
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return JSON.stringify({ tokens : JSON.parse(storage[`oidc.${configurationName}:${redirectUri}`]).tokens });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
saveItemsAsync,
|
|
57
|
+
loadItemsAsync,
|
|
58
|
+
clearAsync,
|
|
59
|
+
initAsync,
|
|
60
|
+
setTokens,
|
|
61
|
+
getTokens,
|
|
62
|
+
setSessionState,
|
|
63
|
+
getSessionState,
|
|
64
|
+
setNonceAsync,
|
|
65
|
+
getNonceAsync
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import timer from "./timer"
|
|
2
|
+
import {parseOriginalTokens} from "./parseTokens";
|
|
3
|
+
|
|
4
|
+
function get_browser() {
|
|
5
|
+
let ua = navigator.userAgent, tem,
|
|
6
|
+
M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
|
7
|
+
if(/trident/i.test(M[1])){
|
|
8
|
+
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
|
|
9
|
+
return {name:'ie',version:(tem[1]||'')};
|
|
10
|
+
}
|
|
11
|
+
if(M[1]==='Chrome'){
|
|
12
|
+
tem=ua.match(/\bOPR|Edge\/(\d+)/);
|
|
13
|
+
|
|
14
|
+
if(tem!=null) {
|
|
15
|
+
let version = tem[1];
|
|
16
|
+
if(!version){
|
|
17
|
+
const splits = ua.split(tem[0]+"/");
|
|
18
|
+
if(splits.length>1){
|
|
19
|
+
version = splits[1];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {name:'opera', version};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
|
|
27
|
+
if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);}
|
|
28
|
+
return {
|
|
29
|
+
name: M[0].toLowerCase(),
|
|
30
|
+
version: M[1]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let keepAliveServiceWorkerTimeoutId = null;
|
|
35
|
+
|
|
36
|
+
export const sleepAsync = (milliseconds) => {
|
|
37
|
+
return new Promise(resolve => timer.setTimeout(resolve, milliseconds))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const keepAlive = () => {
|
|
41
|
+
fetch('/OidcKeepAliveServiceWorker.json');
|
|
42
|
+
sleepAsync(230*1000).then(keepAlive);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isServiceWorkerProxyActiveAsync = () => {
|
|
46
|
+
return fetch('/OidcKeepAliveServiceWorker.json', {
|
|
47
|
+
headers: {
|
|
48
|
+
'oidc-vanilla': "true"
|
|
49
|
+
}})
|
|
50
|
+
.then((response) => {
|
|
51
|
+
return response.statusText === 'oidc-service-worker';
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const sendMessageAsync = (registration) => (data) =>{
|
|
56
|
+
return new Promise(function(resolve, reject) {
|
|
57
|
+
const messageChannel = new MessageChannel();
|
|
58
|
+
messageChannel.port1.onmessage = function (event) {
|
|
59
|
+
if (event.data && event.data.error) {
|
|
60
|
+
reject(event.data.error);
|
|
61
|
+
} else {
|
|
62
|
+
resolve(event.data);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
registration.active.postMessage(data, [messageChannel.port2]);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const initWorkerAsync = async(serviceWorkerRelativeUrl, configurationName) => {
|
|
70
|
+
|
|
71
|
+
if(typeof window === "undefined" || typeof navigator === "undefined" || !navigator.serviceWorker||!serviceWorkerRelativeUrl){
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const {name, version} = get_browser();
|
|
75
|
+
if(name == "chrome" && parseInt(version)<90){
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if(name == "opera"){
|
|
79
|
+
if(!version) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if(parseInt(version.split(".")[0])< 80) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if(name == "ie"){
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const registration = await navigator.serviceWorker.register(serviceWorkerRelativeUrl);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await navigator.serviceWorker.ready
|
|
94
|
+
}
|
|
95
|
+
catch(err) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const saveItemsAsync =(items) =>{
|
|
100
|
+
return sendMessageAsync(registration)({type: "saveItems", data: items, configurationName});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const loadItemsAsync=() =>{
|
|
104
|
+
return sendMessageAsync(registration)({type: "loadItems", data: null, configurationName});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const unregisterAsync = async () => {
|
|
108
|
+
return await registration.unregister();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const clearAsync=(status) =>{
|
|
112
|
+
return sendMessageAsync(registration)({type: "clear", data: {status}, configurationName});
|
|
113
|
+
}
|
|
114
|
+
const initAsync= async (oidcServerConfiguration, where) => {
|
|
115
|
+
const result = await sendMessageAsync(registration)({
|
|
116
|
+
type: "init",
|
|
117
|
+
data: {oidcServerConfiguration, where},
|
|
118
|
+
configurationName
|
|
119
|
+
});
|
|
120
|
+
// @ts-ignore
|
|
121
|
+
return { tokens : parseOriginalTokens(result.tokens, null), status: result.status};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const startKeepAliveServiceWorker = () => {
|
|
125
|
+
if (keepAliveServiceWorkerTimeoutId == null) {
|
|
126
|
+
keepAliveServiceWorkerTimeoutId = "not_null";
|
|
127
|
+
keepAlive();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const setSessionStateAsync = (sessionState) => {
|
|
132
|
+
return sendMessageAsync(registration)({type: "setSessionState", data: {sessionState}, configurationName});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const getSessionStateAsync= async () => {
|
|
136
|
+
const result = await sendMessageAsync(registration)({type: "getSessionState", data: null, configurationName});
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
return result.sessionState;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const setNonceAsync = (nonce) => {
|
|
142
|
+
return sendMessageAsync(registration)({type: "setNonce", data: {nonce}, configurationName});
|
|
143
|
+
}
|
|
144
|
+
const NONCE_TOKEN = 'NONCE_SECURED_BY_OIDC_SERVICE_WORKER';
|
|
145
|
+
const getNonceAsync= async () => {
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
const keyNonce = NONCE_TOKEN + '_'+ configurationName;
|
|
148
|
+
return {nonce:keyNonce};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
saveItemsAsync,
|
|
153
|
+
loadItemsAsync,
|
|
154
|
+
clearAsync,
|
|
155
|
+
initAsync,
|
|
156
|
+
// getAccessTokenPayloadAsync,
|
|
157
|
+
startKeepAliveServiceWorker,
|
|
158
|
+
isServiceWorkerProxyActiveAsync,
|
|
159
|
+
setSessionStateAsync,
|
|
160
|
+
getSessionStateAsync,
|
|
161
|
+
setNonceAsync,
|
|
162
|
+
getNonceAsync,
|
|
163
|
+
unregisterAsync,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export class MemoryStorageBackend {
|
|
2
|
+
public items: any;
|
|
3
|
+
private saveItemsAsync: Function;
|
|
4
|
+
|
|
5
|
+
constructor(saveItemsAsync, items = {}) {
|
|
6
|
+
this.items = items;
|
|
7
|
+
this.saveItemsAsync = saveItemsAsync;
|
|
8
|
+
this.saveItemsAsync.bind(this);
|
|
9
|
+
this.getItem.bind(this);
|
|
10
|
+
this.removeItem.bind(this);
|
|
11
|
+
this.clear.bind(this);
|
|
12
|
+
this.setItem.bind(this);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getItem(name) {
|
|
16
|
+
return Promise.resolve(this.items[name]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
removeItem(name) {
|
|
20
|
+
delete this.items[name];
|
|
21
|
+
return this.saveItemsAsync(this.items);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
clear() {
|
|
25
|
+
this.items = {};
|
|
26
|
+
return this.saveItemsAsync(this.items);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setItem(name, value) {
|
|
30
|
+
this.items[name] = value;
|
|
31
|
+
return this.saveItemsAsync(this.items);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {BasicQueryStringUtils} from '@openid/appauth';
|
|
2
|
+
|
|
3
|
+
export class NoHashQueryStringUtils extends BasicQueryStringUtils {
|
|
4
|
+
parse(input, useHash) {
|
|
5
|
+
const output = super.parse(input, false /* never use hash */);
|
|
6
|
+
return output;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const keys = ["code", "session_state", "state"]
|
|
11
|
+
|
|
12
|
+
export class HashQueryStringUtils extends BasicQueryStringUtils {
|
|
13
|
+
parse(input, useHash) {
|
|
14
|
+
const output = super.parse(input, true /* use hash */);
|
|
15
|
+
|
|
16
|
+
// Fix AppAuthJs behavior
|
|
17
|
+
let propertyToDelelete = null;
|
|
18
|
+
Object.entries(output).map(([key, value]) => {
|
|
19
|
+
keys.forEach(k => {
|
|
20
|
+
if(key.endsWith(`?${k}`)){
|
|
21
|
+
output[k]= value;
|
|
22
|
+
propertyToDelelete = key;
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if(propertyToDelelete){
|
|
28
|
+
delete output[propertyToDelelete];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
}
|