@axa-fr/react-oidc 6.0.0-alpha7 → 6.0.0-beta0
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 +1 -1
- package/dist/OidcServiceWorker.js +8 -10
- package/dist/vanilla/initWorker.d.ts.map +1 -1
- package/dist/vanilla/initWorker.js +2 -3
- package/dist/vanilla/initWorker.js.map +1 -1
- package/dist/vanilla/oidc.d.ts.map +1 -1
- package/dist/vanilla/oidc.js +126 -102
- package/dist/vanilla/oidc.js.map +1 -1
- package/package.json +2 -2
- package/src/oidc/vanilla/OidcServiceWorker.js +8 -10
- package/src/oidc/vanilla/initWorker.ts +2 -3
- package/src/oidc/vanilla/oidc.ts +73 -73
- package/src/App.css +0 -38
- package/src/App.specold.tsx +0 -46
- package/src/App.tsx +0 -96
- package/src/FetchUser.tsx +0 -53
- package/src/Home.tsx +0 -22
- package/src/MultiAuth.tsx +0 -116
- package/src/Profile.tsx +0 -77
- package/src/configurations.ts +0 -70
- package/src/index.css +0 -13
- package/src/index.tsx +0 -9
- package/src/logo.svg +0 -7
- package/src/override/AuthenticateError.component.tsx +0 -14
- package/src/override/Authenticating.component.tsx +0 -14
- package/src/override/Callback.component.tsx +0 -13
- package/src/override/Loading.component.tsx +0 -13
- package/src/override/ServiceWorkerNotSupported.component.tsx +0 -15
- package/src/override/SessionLost.component.tsx +0 -21
- package/src/override/style.ts +0 -10
- package/src/setupTests.js +0 -5
|
@@ -125,11 +125,11 @@ const keepAliveAsync = async (event) => {
|
|
|
125
125
|
const init = {"status": 200, "statusText": 'oidc-service-worker'};
|
|
126
126
|
const response = new Response('{}', init);
|
|
127
127
|
if(!isFromVanilla) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
for(let i=0; i<240;i++){
|
|
129
|
+
await sleep(1000 + Math.floor(Math.random() * 1000));
|
|
130
|
+
const cache = await caches.open("oidc_dummy_cache");
|
|
131
|
+
await cache.put(event.request, response.clone());
|
|
132
|
+
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
return response;
|
|
@@ -175,9 +175,7 @@ const handleFetch = async (event) => {
|
|
|
175
175
|
break;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
console.log("currentDatabase");
|
|
180
|
-
console.log(currentDatabase);
|
|
178
|
+
|
|
181
179
|
return fetch(originalRequest, {
|
|
182
180
|
body: newBody,
|
|
183
181
|
method: originalRequest.method,
|
|
@@ -191,7 +189,7 @@ const handleFetch = async (event) => {
|
|
|
191
189
|
credentials: originalRequest.credentials,
|
|
192
190
|
integrity: originalRequest.integrity
|
|
193
191
|
}).then(hideTokens(currentDatabase));
|
|
194
|
-
} else if(currentLoginCallbackConfigurationName){
|
|
192
|
+
} else if(actualBody.includes("code_verifier=") && currentLoginCallbackConfigurationName){
|
|
195
193
|
currentDatabase = database[currentLoginCallbackConfigurationName];
|
|
196
194
|
currentLoginCallbackConfigurationName=null;
|
|
197
195
|
return fetch(originalRequest,{
|
|
@@ -294,7 +292,7 @@ addEventListener('message', event => {
|
|
|
294
292
|
...currentDatabase.tokens,
|
|
295
293
|
access_token: ACCESS_TOKEN + "_" + configurationName
|
|
296
294
|
};
|
|
297
|
-
if(
|
|
295
|
+
if(tokens.refresh_token){
|
|
298
296
|
tokens.refresh_token = REFRESH_TOKEN + "_" + configurationName;
|
|
299
297
|
}
|
|
300
298
|
port.postMessage({
|
|
@@ -37,9 +37,8 @@ export const sleepAsync = (milliseconds) => {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const keepAlive = () => {
|
|
40
|
-
fetch('/OidcKeepAliveServiceWorker.json')
|
|
41
|
-
|
|
42
|
-
})
|
|
40
|
+
fetch('/OidcKeepAliveServiceWorker.json');
|
|
41
|
+
sleepAsync(230*1000).then(keepAlive);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
const isServiceWorkerProxyActiveAsync = () => {
|
package/src/oidc/vanilla/oidc.ts
CHANGED
|
@@ -20,6 +20,45 @@ import {CheckSessionIFrame} from "./checkSessionIFrame"
|
|
|
20
20
|
import {getParseQueryStringFromLocation} from "./route-utils";
|
|
21
21
|
import {AuthorizationServiceConfigurationJson} from "@openid/appauth/src/authorization_service_configuration";
|
|
22
22
|
|
|
23
|
+
const performTokenRequestAsync= async (url, details, extras) => {
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
for (let [key, value] of Object.entries(extras)) {
|
|
27
|
+
if (details[key] === undefined) {
|
|
28
|
+
details[key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let formBody = [];
|
|
33
|
+
for (const property in details) {
|
|
34
|
+
const encodedKey = encodeURIComponent(property);
|
|
35
|
+
const encodedValue = encodeURIComponent(details[property]);
|
|
36
|
+
formBody.push(`${encodedKey}=${encodedValue}`);
|
|
37
|
+
}
|
|
38
|
+
const formBodyString = formBody.join("&");
|
|
39
|
+
|
|
40
|
+
const response = await internalFetch(url, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
44
|
+
},
|
|
45
|
+
body: formBodyString,
|
|
46
|
+
});
|
|
47
|
+
if(response.status !== 200){
|
|
48
|
+
return {success:false, status: response.status}
|
|
49
|
+
}
|
|
50
|
+
const result = await response.json();
|
|
51
|
+
return { success : true,
|
|
52
|
+
data : {
|
|
53
|
+
accessToken: result.access_token,
|
|
54
|
+
expiresIn: result.expires_in,
|
|
55
|
+
idToken: result.id_token,
|
|
56
|
+
refreshToken: result.refresh_token,
|
|
57
|
+
scope: result.scope,
|
|
58
|
+
tokenType: result.token_type,
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
23
62
|
|
|
24
63
|
const internalFetch = async (url, headers, numberRetry=0) => {
|
|
25
64
|
let response;
|
|
@@ -30,7 +69,7 @@ const internalFetch = async (url, headers, numberRetry=0) => {
|
|
|
30
69
|
() => reject(new Error('Timeout')), 10000))]);
|
|
31
70
|
} catch (e) {
|
|
32
71
|
if (e.message === 'Timeout'
|
|
33
|
-
|| e.message === 'Network request failed'
|
|
72
|
+
|| e.message === 'Network request failed') {
|
|
34
73
|
if(numberRetry <=2) {
|
|
35
74
|
return await internalFetch(url, headers, numberRetry + 1);
|
|
36
75
|
}
|
|
@@ -205,7 +244,8 @@ const userInfoAsync = async (oidc) => {
|
|
|
205
244
|
const fetchUserInfo = async (accessToken) => {
|
|
206
245
|
const res = await fetch(url, {
|
|
207
246
|
headers: {
|
|
208
|
-
authorization: `Bearer ${accessToken}
|
|
247
|
+
authorization: `Bearer ${accessToken}`,
|
|
248
|
+
credentials: 'same-origin'
|
|
209
249
|
}
|
|
210
250
|
});
|
|
211
251
|
|
|
@@ -377,13 +417,13 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
377
417
|
static eventNames = eventNames;
|
|
378
418
|
|
|
379
419
|
silentSigninCallbackFromIFrame(){
|
|
380
|
-
if (this.configuration.silent_redirect_uri) {
|
|
420
|
+
if (this.configuration.silent_redirect_uri && this.configuration.silent_signin_uri) {
|
|
381
421
|
const queryParams = getParseQueryStringFromLocation(window.location.href);
|
|
382
422
|
window.top.postMessage(`${this.configurationName}_oidc_tokens:${JSON.stringify({tokens:this.tokens, sessionState:queryParams.session_state})}`, window.location.origin);
|
|
383
423
|
}
|
|
384
424
|
}
|
|
385
425
|
silentSigninErrorCallbackFromIFrame(){
|
|
386
|
-
if (this.configuration.silent_redirect_uri) {
|
|
426
|
+
if (this.configuration.silent_redirect_uri && this.configuration.silent_signin_uri) {
|
|
387
427
|
const queryParams = getParseQueryStringFromLocation(window.location.href);
|
|
388
428
|
window.top.postMessage(`${this.configurationName}_oidc_error:${JSON.stringify({error:queryParams.error})}`, window.location.origin);
|
|
389
429
|
}
|
|
@@ -870,13 +910,8 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
870
910
|
}
|
|
871
911
|
|
|
872
912
|
async refreshTokensAsync(refreshToken) {
|
|
873
|
-
|
|
874
|
-
/*while (document.hidden) {
|
|
875
|
-
await sleepAsync(1000);
|
|
876
|
-
this.publishEvent(eventNames.refreshTokensAsync, {message:"wait because document is hidden"});
|
|
877
|
-
}*/
|
|
878
913
|
|
|
879
|
-
const localSilentSigninAsync= async (
|
|
914
|
+
const localSilentSigninAsync= async () => {
|
|
880
915
|
try {
|
|
881
916
|
const silent_token_response = await this.silentSigninAsync();
|
|
882
917
|
if (silent_token_response) {
|
|
@@ -889,13 +924,10 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
889
924
|
timer.clearTimeout(this.timeoutId);
|
|
890
925
|
this.timeoutId=null;
|
|
891
926
|
}
|
|
892
|
-
this.publishEvent(eventNames.refreshTokensAsync_error,
|
|
927
|
+
this.publishEvent(eventNames.refreshTokensAsync_error, {message: "refresh token and silent refresh failed"});
|
|
893
928
|
return null;
|
|
894
929
|
}
|
|
895
|
-
|
|
896
|
-
try{
|
|
897
|
-
this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken});
|
|
898
|
-
|
|
930
|
+
|
|
899
931
|
const configuration = this.configuration;
|
|
900
932
|
const clientId = configuration.client_id;
|
|
901
933
|
const redirectUri = configuration.redirect_uri;
|
|
@@ -906,7 +938,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
906
938
|
return await localSilentSigninAsync();
|
|
907
939
|
}
|
|
908
940
|
|
|
909
|
-
|
|
910
941
|
let extras = {};
|
|
911
942
|
if(configuration.token_request_extras) {
|
|
912
943
|
for (let [key, value] of Object.entries(configuration.token_request_extras)) {
|
|
@@ -914,70 +945,39 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
914
945
|
}
|
|
915
946
|
}
|
|
916
947
|
const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
// use the token response to make a request for an access token
|
|
920
|
-
const request = new TokenRequest({
|
|
948
|
+
|
|
949
|
+
const details = {
|
|
921
950
|
client_id: clientId,
|
|
922
951
|
redirect_uri: redirectUri,
|
|
923
952
|
grant_type: GRANT_TYPE_REFRESH_TOKEN,
|
|
924
|
-
code: undefined,
|
|
925
953
|
refresh_token: refreshToken,
|
|
926
|
-
|
|
927
|
-
});
|
|
954
|
+
};
|
|
928
955
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
};
|
|
939
|
-
|
|
940
|
-
for (let [key, value] of Object.entries(extras)) {
|
|
941
|
-
if (details[key] === undefined) {
|
|
942
|
-
details[key] = value;
|
|
956
|
+
let index = 0;
|
|
957
|
+
while (index <=2) {
|
|
958
|
+
try {
|
|
959
|
+
this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
|
|
960
|
+
if(index > 1) {
|
|
961
|
+
while (document.hidden) {
|
|
962
|
+
await sleepAsync(1000);
|
|
963
|
+
this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
|
|
964
|
+
}
|
|
943
965
|
}
|
|
966
|
+
const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
|
|
967
|
+
if (tokenResponse.success) {
|
|
968
|
+
this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
|
|
969
|
+
return tokenResponse.data;
|
|
970
|
+
} else {
|
|
971
|
+
this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "bad request" , tokenResponse: tokenResponse});
|
|
972
|
+
return await localSilentSigninAsync();
|
|
973
|
+
}
|
|
974
|
+
} catch (exception) {
|
|
975
|
+
console.error(exception);
|
|
976
|
+
this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exception" ,exception: exception});
|
|
944
977
|
}
|
|
945
|
-
|
|
946
|
-
let formBody = [];
|
|
947
|
-
for (const property in details) {
|
|
948
|
-
const encodedKey = encodeURIComponent(property);
|
|
949
|
-
const encodedValue = encodeURIComponent(details[property]);
|
|
950
|
-
formBody.push(`${encodedKey}=${encodedValue}`);
|
|
951
|
-
}
|
|
952
|
-
const formBodyString = formBody.join("&");
|
|
953
|
-
|
|
954
|
-
const response = await internalFetch(url, {
|
|
955
|
-
method: 'POST',
|
|
956
|
-
headers: {
|
|
957
|
-
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
958
|
-
},
|
|
959
|
-
body: formBodyString,
|
|
960
|
-
});
|
|
961
|
-
const result = await response.json();
|
|
962
|
-
return {
|
|
963
|
-
accessToken: result.access_token,
|
|
964
|
-
expiresIn: result.expires_in,
|
|
965
|
-
idToken: result.id_token,
|
|
966
|
-
refreshToken: result.refresh_token,
|
|
967
|
-
scope: result.scope,
|
|
968
|
-
tokenType: result.token_type,
|
|
969
|
-
};
|
|
978
|
+
index++;
|
|
970
979
|
}
|
|
971
|
-
|
|
972
|
-
const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint)
|
|
973
|
-
|
|
974
|
-
this.publishEvent(eventNames.refreshTokensAsync_end, {message:"success"});
|
|
975
|
-
return tokenResponse;
|
|
976
|
-
} catch(exception) {
|
|
977
|
-
console.error(exception);
|
|
978
|
-
this.publishEvent(eventNames.refreshTokensAsync_silent_error, exception);
|
|
979
|
-
return await localSilentSigninAsync(exception);
|
|
980
|
-
}
|
|
980
|
+
|
|
981
981
|
}
|
|
982
982
|
|
|
983
983
|
syncTokensAsyncPromise=null;
|
|
@@ -1004,7 +1004,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
|
|
|
1004
1004
|
if (silent_token_response && silent_token_response.tokens) {
|
|
1005
1005
|
this.tokens = await setTokensAsync(serviceWorker, silent_token_response.tokens);
|
|
1006
1006
|
} else{
|
|
1007
|
-
this.publishEvent(eventNames.syncTokensAsync_error,
|
|
1007
|
+
this.publishEvent(eventNames.syncTokensAsync_error, {message:"no token found in result"});
|
|
1008
1008
|
if(this.timeoutId){
|
|
1009
1009
|
timer.clearTimeout(this.timeoutId);
|
|
1010
1010
|
this.timeoutId=null;
|
package/src/App.css
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
.App {
|
|
2
|
-
text-align: center;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.App-logo {
|
|
6
|
-
height: 40vmin;
|
|
7
|
-
pointer-events: none;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
11
|
-
.App-logo {
|
|
12
|
-
animation: App-logo-spin infinite 20s linear;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.App-header {
|
|
17
|
-
background-color: #282c34;
|
|
18
|
-
min-height: 100vh;
|
|
19
|
-
display: flex;
|
|
20
|
-
flex-direction: column;
|
|
21
|
-
align-items: center;
|
|
22
|
-
justify-content: center;
|
|
23
|
-
font-size: calc(10px + 2vmin);
|
|
24
|
-
color: white;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.App-link {
|
|
28
|
-
color: #61dafb;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@keyframes App-logo-spin {
|
|
32
|
-
from {
|
|
33
|
-
transform: rotate(0deg);
|
|
34
|
-
}
|
|
35
|
-
to {
|
|
36
|
-
transform: rotate(360deg);
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/App.specold.tsx
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// __tests__/fetch.test.js
|
|
2
|
-
/*import React from 'react'
|
|
3
|
-
import {rest} from 'msw'
|
|
4
|
-
import {setupServer} from 'msw/node'
|
|
5
|
-
import {render, fireEvent, waitFor, screen} from '@testing-library/react'
|
|
6
|
-
import '@testing-library/jest-dom'
|
|
7
|
-
import App from "./App";
|
|
8
|
-
import {act} from "react-dom/test-utils";
|
|
9
|
-
|
|
10
|
-
const server = setupServer(
|
|
11
|
-
rest.get('http://api/.well-known/openid-configuration', (req, res, ctx) => {
|
|
12
|
-
return res( ctx.status(200),ctx.json({
|
|
13
|
-
"issuer":"https://demo.identityserver.io",
|
|
14
|
-
"jwks_uri":"https://demo.identityserver.io/.well-known/openid-configuration/jwks",
|
|
15
|
-
"authorization_endpoint":"https://demo.identityserver.io/connect/authorize",
|
|
16
|
-
"token_endpoint":"https://demo.identityserver.io/connect/token",
|
|
17
|
-
"userinfo_endpoint":"https://demo.identityserver.io/connect/userinfo",
|
|
18
|
-
"end_session_endpoint":"https://demo.identityserver.io/connect/endsession",
|
|
19
|
-
"check_session_iframe":"https://demo.identityserver.io/connect/checksession",
|
|
20
|
-
"revocation_endpoint":"https://demo.identityserver.io/connect/revocation",
|
|
21
|
-
"introspection_endpoint":"https://demo.identityserver.io/connect/introspect",
|
|
22
|
-
"device_authorization_endpoint":"https://demo.identityserver.io/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","profile","email","api","api.scope1","api.scope2","scope2","policyserver.runtime","policyserver.management","offline_access"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at","email","email_verified"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}))
|
|
23
|
-
}),
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
beforeAll(() => server.listen())
|
|
27
|
-
afterEach(() => server.resetHandlers())
|
|
28
|
-
afterAll(() => server.close())
|
|
29
|
-
|
|
30
|
-
test('Load home page then login should log', async () => {
|
|
31
|
-
|
|
32
|
-
const configuration = {
|
|
33
|
-
client_id: 'interactive.public.short',
|
|
34
|
-
redirect_uri: 'http://localhost:4200/authentication/callback',
|
|
35
|
-
scope: 'openid profile email api offline_access',
|
|
36
|
-
authority: 'http://api',
|
|
37
|
-
refresh_time_before_tokens_expiration_in_second: 70,
|
|
38
|
-
};
|
|
39
|
-
// @ts-ignore
|
|
40
|
-
const {debug, getByText, rerender} = render(<App configuration={configuration}/>);
|
|
41
|
-
await waitFor(() => getByText('React Demo Application protected by OpenId Connect'));
|
|
42
|
-
fireEvent.click(screen.getByText('Login'));
|
|
43
|
-
await waitFor(() => getByText('Authentification en cours'));
|
|
44
|
-
|
|
45
|
-
})
|
|
46
|
-
*/
|
package/src/App.tsx
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import React, {useReducer} from 'react';
|
|
2
|
-
import {BrowserRouter, Route, Link, Routes} from 'react-router-dom';
|
|
3
|
-
import { Home } from "./Home";
|
|
4
|
-
import { Profile, SecureProfile } from "./Profile";
|
|
5
|
-
import { configurationAuth0, configurationIdentityServer, configurationIdentityServerWithoutDiscovery } from './configurations';
|
|
6
|
-
import { withOidcSecure, OidcProvider } from "./oidc";
|
|
7
|
-
import {FetchUserHoc, FetchUserHook} from "./FetchUser";
|
|
8
|
-
import { MultiAuthContainer } from "./MultiAuth";
|
|
9
|
-
|
|
10
|
-
const OidcSecureHoc = withOidcSecure(Profile);
|
|
11
|
-
|
|
12
|
-
function reducer(state, action) {
|
|
13
|
-
switch (action.type) {
|
|
14
|
-
case 'event':
|
|
15
|
-
return [{...action.data, date:Date.now()}, ...state]
|
|
16
|
-
default:
|
|
17
|
-
throw new Error();
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function App() {
|
|
22
|
-
const [show, setShow] = React.useState(false);
|
|
23
|
-
const [events, dispatch] = useReducer(reducer, []);
|
|
24
|
-
|
|
25
|
-
const onEvent=(configurationName, eventName, data )=>{
|
|
26
|
-
// console.log(`oidc:${configurationName}:${eventName}`, data);
|
|
27
|
-
dispatch({type: 'event', data: {name: `oidc:${configurationName}:${eventName}`, data}})
|
|
28
|
-
}
|
|
29
|
-
return (<>
|
|
30
|
-
|
|
31
|
-
<OidcProvider configuration={configurationIdentityServer} onEvent={onEvent}>
|
|
32
|
-
<BrowserRouter>
|
|
33
|
-
<nav className="navbar navbar-expand-lg navbar-dark bg-primary">
|
|
34
|
-
<a className="navbar-brand" href="/">@axa-fr/react-oidc</a>
|
|
35
|
-
<button className="navbar-toggler" type="button" onClick={() => setShow(!show)} data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
36
|
-
<span className="navbar-toggler-icon"/>
|
|
37
|
-
</button>
|
|
38
|
-
<div style={show ? { display: "block" } : { display: 'none' }} className="collapse navbar-collapse" id="navbarNav">
|
|
39
|
-
<ul className="navbar-nav">
|
|
40
|
-
<li className="nav-item">
|
|
41
|
-
<Link className="nav-link" to="/">Home</Link>
|
|
42
|
-
</li>
|
|
43
|
-
<li className="nav-item">
|
|
44
|
-
<Link className="nav-link" to="/profile">Profile</Link>
|
|
45
|
-
</li>
|
|
46
|
-
<li className="nav-item">
|
|
47
|
-
<Link className="nav-link" to="/profile-secure-component">Secure Profile Component</Link>
|
|
48
|
-
</li>
|
|
49
|
-
<li className="nav-item">
|
|
50
|
-
<Link className="nav-link" to="/profile-secure-hoc">Secure Profile Hoc</Link>
|
|
51
|
-
</li>
|
|
52
|
-
<li className="nav-item">
|
|
53
|
-
<Link className="nav-link" to="/user-fetch-secure-hoc">Secure User Fetch Hoc</Link>
|
|
54
|
-
</li>
|
|
55
|
-
<li className="nav-item">
|
|
56
|
-
<Link className="nav-link" to="/user-fetch-secure-hook">Secure User Fetch Hook</Link>
|
|
57
|
-
</li>
|
|
58
|
-
<li className="nav-item">
|
|
59
|
-
<Link className="nav-link" to="/multi-auth">Multi Auth</Link>
|
|
60
|
-
</li>
|
|
61
|
-
</ul>
|
|
62
|
-
</div>
|
|
63
|
-
</nav>
|
|
64
|
-
|
|
65
|
-
<div>
|
|
66
|
-
<Routes>
|
|
67
|
-
<Route path="/" element={<Home></Home>} />
|
|
68
|
-
<Route path="/profile" element={<Profile></Profile>} />
|
|
69
|
-
<Route path="/profile-secure-component" element={<SecureProfile></SecureProfile>} />
|
|
70
|
-
<Route path="/profile-secure-hoc" element={<OidcSecureHoc></OidcSecureHoc>} />
|
|
71
|
-
<Route path="/user-fetch-secure-hoc" element={<FetchUserHoc></FetchUserHoc>} />
|
|
72
|
-
<Route path="/user-fetch-secure-hook" element={<FetchUserHook></FetchUserHook>} />
|
|
73
|
-
<Route path="/multi-auth/*" element={<MultiAuthContainer></MultiAuthContainer>} />
|
|
74
|
-
</Routes>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
</BrowserRouter>
|
|
78
|
-
</OidcProvider>
|
|
79
|
-
<div className="container-fluid mt-3">
|
|
80
|
-
<div className="card">
|
|
81
|
-
<div className="card-body" >
|
|
82
|
-
<h5 className="card-title">Default configuration Events</h5>
|
|
83
|
-
<div style={{"overflowX": "hidden", "overflowY": "scroll", "maxHeight": "400px"}}>
|
|
84
|
-
{events.map(e => {
|
|
85
|
-
const date = new Date(e.date);
|
|
86
|
-
const dateFormated = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
|
|
87
|
-
return <p>{dateFormated} {e.name}: { JSON.stringify(e.data)}</p>
|
|
88
|
-
})}
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div></>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export default App;
|
package/src/FetchUser.tsx
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import React, {useEffect, useState} from 'react';
|
|
2
|
-
|
|
3
|
-
import {useOidcFetch, withOidcFetch} from "./oidc/FetchToken";
|
|
4
|
-
import {OidcSecure} from "./oidc";
|
|
5
|
-
|
|
6
|
-
const DisplayUserInfo = ({ fetch }) => {
|
|
7
|
-
const [oidcUser, setOidcUser] = useState(null);
|
|
8
|
-
const [isLoading, setLoading] = useState(true);
|
|
9
|
-
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const fetchUserInfoAsync = async () => {
|
|
12
|
-
const res = await fetch("https://demo.duendesoftware.com/connect/userinfo");
|
|
13
|
-
if (res.status != 200) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
return res.json();
|
|
17
|
-
};
|
|
18
|
-
let isMounted = true;
|
|
19
|
-
fetchUserInfoAsync().then((userInfo) => {
|
|
20
|
-
if(isMounted) {
|
|
21
|
-
setLoading(false);
|
|
22
|
-
setOidcUser(userInfo)
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
return () => {
|
|
26
|
-
isMounted = false;
|
|
27
|
-
};
|
|
28
|
-
},[]);
|
|
29
|
-
|
|
30
|
-
if(isLoading){
|
|
31
|
-
return <>Loading</>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="container mt-3">
|
|
36
|
-
<div className="card text-white bg-success mb-3">
|
|
37
|
-
<div className="card-body">
|
|
38
|
-
<h5 className="card-title">User information</h5>
|
|
39
|
-
{oidcUser != null && <p className="card-text">{JSON.stringify(oidcUser)}</p>}
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const UserInfoWithFetchHoc = withOidcFetch(fetch)(DisplayUserInfo);
|
|
47
|
-
|
|
48
|
-
export const FetchUserHoc= () => <OidcSecure><UserInfoWithFetchHoc/></OidcSecure>;
|
|
49
|
-
|
|
50
|
-
export const FetchUserHook= () => {
|
|
51
|
-
const {fetch} = useOidcFetch();
|
|
52
|
-
return <OidcSecure><DisplayUserInfo fetch={fetch} /></OidcSecure>
|
|
53
|
-
}
|
package/src/Home.tsx
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {useOidc} from "./oidc";
|
|
3
|
-
|
|
4
|
-
export const Home = () => {
|
|
5
|
-
|
|
6
|
-
const { login, logout, isAuthenticated} = useOidc();
|
|
7
|
-
|
|
8
|
-
return (
|
|
9
|
-
<div className="container-fluid mt-3">
|
|
10
|
-
<div className="card">
|
|
11
|
-
<div className="card-body">
|
|
12
|
-
<h5 className="card-title">Home</h5>
|
|
13
|
-
<p className="card-text">React Demo Application protected by OpenId Connect. More info on about oidc on <a href="https://github.com/AxaGuilDEv/react-oidc">GitHub @axa-fr/react-oidc</a></p>
|
|
14
|
-
{!isAuthenticated && <p><button type="button" className="btn btn-primary" onClick={() => login('/profile')}>Login</button></p>}
|
|
15
|
-
{!isAuthenticated && <p><button type="button" className="btn btn-primary" onClick={() => login('/profile', null, "youhou")}>Login with state</button></p>}
|
|
16
|
-
{isAuthenticated && <p><button type="button" className="btn btn-primary" onClick={() => logout('/profile')}>logout /profile</button></p>}
|
|
17
|
-
{isAuthenticated && <p><button type="button" className="btn btn-primary" onClick={() => logout('')}>logout</button></p>}
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
)
|
|
22
|
-
};
|
package/src/MultiAuth.tsx
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import React, {useState} from 'react';
|
|
2
|
-
import {OidcProvider, useOidc, useOidcAccessToken, useOidcIdToken} from "./oidc";
|
|
3
|
-
import { configurationIdentityServer, configurationIdentityServerWithHash, configurationGoogle} from "./configurations";
|
|
4
|
-
import AuthenticatingError from "./override/AuthenticateError.component"
|
|
5
|
-
import Authenticating from "./override/Authenticating.component"
|
|
6
|
-
import Loading from "./override/Loading.component"
|
|
7
|
-
import {CallBackSuccess} from "./override/Callback.component"
|
|
8
|
-
import SessionLost from "./override/SessionLost.component"
|
|
9
|
-
import ServiceWorkerNotSupported from "./override/ServiceWorkerNotSupported.component"
|
|
10
|
-
|
|
11
|
-
const MultiAuth = ( {configurationName, handleConfigurationChange }) => {
|
|
12
|
-
const { login, logout, isAuthenticated} = useOidc(configurationName);
|
|
13
|
-
const [fname, setFname] = useState("")
|
|
14
|
-
|
|
15
|
-
const handleChange = e => {
|
|
16
|
-
setFname(e.target.value)
|
|
17
|
-
}
|
|
18
|
-
return (
|
|
19
|
-
<div className="container-fluid mt-3">
|
|
20
|
-
<div className="card">
|
|
21
|
-
<div className="card-body">
|
|
22
|
-
<h5 className="card-title">Multiple Authentication</h5>
|
|
23
|
-
<form>
|
|
24
|
-
<label>
|
|
25
|
-
First Name:{" "}
|
|
26
|
-
<input type="text" value={fname} onChange={handleChange} />
|
|
27
|
-
</label>
|
|
28
|
-
</form>
|
|
29
|
-
<p className="card-text">React Demo Application protected by OpenId Connect with MultipleAuthentication.
|
|
30
|
-
<br/>For example, config_1 can have other sensitive scope, config_2 does not ask for the "offline_access" so it does not retrieve the most sensitive token "refresh_token" for very sensitive operation, it retrive only access_token valid for a small amout of time.</p>
|
|
31
|
-
<select value={configurationName} onChange={handleConfigurationChange} >
|
|
32
|
-
<option value="config_classic">config_classic</option>
|
|
33
|
-
<option value="config_without_refresh_token">config_without_refresh_token</option>
|
|
34
|
-
<option value="config_google">google</option>
|
|
35
|
-
<option value="config_with_hash">config_with_hash</option>
|
|
36
|
-
</select>
|
|
37
|
-
{!isAuthenticated && <button type="button" className="btn btn-primary" onClick={() => login()}>Login</button>}
|
|
38
|
-
{isAuthenticated && <button type="button" className="btn btn-primary" onClick={() => logout()}>logout</button>}
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
if(!sessionStorage.configurationName){
|
|
46
|
-
sessionStorage.configurationName = "config_classic";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const MultiAuthContainer = () => {
|
|
50
|
-
const [isSessionLost, setIsSessionLost] = useState(false)
|
|
51
|
-
const [configurationName, setConfigurationName] = useState(sessionStorage.configurationName);
|
|
52
|
-
const callBack = window.location.origin+"/multi-auth/authentification/callback2";
|
|
53
|
-
const silent_redirect_uri = window.location.origin+"/multi-auth/authentification/silent-callback2";
|
|
54
|
-
const configurations = {
|
|
55
|
-
config_classic: {...configurationIdentityServer,
|
|
56
|
-
redirect_uri:callBack,
|
|
57
|
-
silent_redirect_uri,
|
|
58
|
-
scope: 'openid profile email api offline_access'
|
|
59
|
-
},
|
|
60
|
-
config_without_refresh_token: {...configurationIdentityServer,
|
|
61
|
-
redirect_uri:callBack,
|
|
62
|
-
silent_redirect_uri: "",
|
|
63
|
-
scope: 'openid profile email api'},
|
|
64
|
-
config_google: { ...configurationGoogle },
|
|
65
|
-
config_with_hash: { ...configurationIdentityServerWithHash}
|
|
66
|
-
}
|
|
67
|
-
const handleConfigurationChange = (event) => {
|
|
68
|
-
const configurationName = event.target.value;
|
|
69
|
-
sessionStorage.configurationName = configurationName;
|
|
70
|
-
setConfigurationName(configurationName);
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const onSessionLost = ()=>{
|
|
75
|
-
setIsSessionLost(true);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<>
|
|
80
|
-
<OidcProvider configuration={configurations[configurationName]}
|
|
81
|
-
configurationName={configurationName}
|
|
82
|
-
loadingComponent={Loading}
|
|
83
|
-
authenticatingErrorComponent={AuthenticatingError}
|
|
84
|
-
authenticatingComponent={Authenticating}
|
|
85
|
-
serviceWorkerNotSupportedComponent={ServiceWorkerNotSupported}
|
|
86
|
-
callbackSuccessComponent={CallBackSuccess}
|
|
87
|
-
onSessionLost={onSessionLost}
|
|
88
|
-
>
|
|
89
|
-
{ isSessionLost && <SessionLost configurationName={configurationName}/>}
|
|
90
|
-
<MultiAuth configurationName={configurationName} handleConfigurationChange={handleConfigurationChange} />
|
|
91
|
-
<DisplayAccessToken configurationName={configurationName} />
|
|
92
|
-
</OidcProvider>
|
|
93
|
-
</>
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const DisplayAccessToken = ({configurationName}) => {
|
|
98
|
-
const{ accessToken, accessTokenPayload } = useOidcAccessToken(configurationName);
|
|
99
|
-
const{ idTokenPayload } = useOidcIdToken(configurationName);
|
|
100
|
-
|
|
101
|
-
if(!accessToken){
|
|
102
|
-
return <p>you are not authentified</p>
|
|
103
|
-
}
|
|
104
|
-
return (
|
|
105
|
-
<div className="card text-white bg-info mb-3">
|
|
106
|
-
<div className="card-body">
|
|
107
|
-
<h5 className="card-title">Access Token</h5>
|
|
108
|
-
<p style={{color:'red', "backgroundColor": 'white'}}>Please consider to configure the ServiceWorker in order to protect your application from XSRF attacks. "access_token" and "refresh_token" will never be accessible from your client side javascript.</p>
|
|
109
|
-
{<p className="card-text">Access Token: {JSON.stringify(accessToken)}</p>}
|
|
110
|
-
{accessTokenPayload != null && <p className="card-text">Access Token Payload: {JSON.stringify(accessTokenPayload)}</p>}
|
|
111
|
-
<h5 className="card-title">Id Token</h5>
|
|
112
|
-
{idTokenPayload != null && <p className="card-text">Access Token Payload: {JSON.stringify(idTokenPayload)}</p>}
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
)
|
|
116
|
-
};
|