@axa-fr/react-oidc 6.9.4 → 6.9.6
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 +260 -219
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# @axa-fr/react-oidc
|
|
2
2
|
|
|
3
|
-
Try the demo at https://black-rock-0dc6b0d03.1.azurestaticapps.net
|
|
3
|
+
Try the demo at <https://black-rock-0dc6b0d03.1.azurestaticapps.net/>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
<img src="https://github.com/AxaGuilDEv/react-oidc/blob/master/docs/img/introduction.gif?raw=true"
|
|
7
|
-
alt="Sample React Oicd"
|
|
8
|
-
/>
|
|
9
|
-
</p>
|
|
5
|
+

|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
A set of react components to make Oidc (OpenID Connect) client easy. It aim to simplify OAuth authentication between multiples providers.
|
|
13
|
-
</p>
|
|
7
|
+
A set of react components to make OIDC (OpenID Connect) client easy. It aim to simplify OAuth authentication between multiples providers.
|
|
14
8
|
|
|
15
9
|
- [About](#about)
|
|
16
10
|
- [Getting Started](#getting-started)
|
|
@@ -21,16 +15,18 @@ Try the demo at https://black-rock-0dc6b0d03.1.azurestaticapps.net/
|
|
|
21
15
|
- [Hash route](#Hash-route)
|
|
22
16
|
- [Service Worker Support](#service-worker-support)
|
|
23
17
|
|
|
18
|
+
## About
|
|
19
|
+
|
|
24
20
|
Easy set up of OIDC for react.
|
|
25
|
-
It use AppAuthJS behind the scene because it very lightweight and created by
|
|
21
|
+
It use AppAuthJS behind the scene because it very lightweight and created by OpenID certification team. `oidc-client` that was used in V3 was heavy and not longer maintained.
|
|
26
22
|
|
|
27
23
|
- **Secure** :
|
|
28
|
-
- With the use of Service Worker, your tokens (refresh_token and access_token) are not accessible to the
|
|
29
|
-
- OIDC using client side Code Credential Grant with
|
|
24
|
+
- With the use of Service Worker, your tokens (refresh_token and access_token) are not accessible to the JavaScript client code (big protection against XSRF attacks)
|
|
25
|
+
- OIDC using client side Code Credential Grant with PKCE only
|
|
30
26
|
- **Lightweight**
|
|
31
27
|
- **Simple** :
|
|
32
28
|
- refresh_token and access_token are auto refreshed in background
|
|
33
|
-
- with the use of the Service Worker, you do not need to inject the access_token in every fetch, you have only to configure OidcTrustedDomains.js file
|
|
29
|
+
- with the use of the Service Worker, you do not need to inject the access_token in every fetch, you have only to configure `OidcTrustedDomains.js` file
|
|
34
30
|
- **No cookies problem** : You can disable silent signin (that internally use an iframe). For your information, your OIDC server should be in the same domain of your website in order to be able to send OIDC server cookies from your website via an internal IFRAME, else, you may encounter COOKIES problem.
|
|
35
31
|
- **Multiple Authentication** :
|
|
36
32
|
- You can authenticate many times to the same provider with different scope (for example you can acquire a new 'payment' scope for a payment)
|
|
@@ -38,15 +34,11 @@ It use AppAuthJS behind the scene because it very lightweight and created by ope
|
|
|
38
34
|
- **Flexible** :
|
|
39
35
|
- Work with Service Worker (more secure) and without for older browser (less secure)
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/>
|
|
45
|
-
<br>
|
|
46
|
-
The service worker catch <b>access_token</b> and <b>refresh_token</b> that will never be accessible to the client.
|
|
47
|
-
</p>
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
The service worker catch **access_token** and **refresh_token** that will never be accessible to the client.
|
|
48
40
|
|
|
49
|
-
|
|
41
|
+
## Getting Started
|
|
50
42
|
|
|
51
43
|
```sh
|
|
52
44
|
npm install @axa-fr/react-oidc --save
|
|
@@ -63,17 +55,16 @@ The only file you should edit is "OidcTrustedDomains.js".
|
|
|
63
55
|
// OidcTrustedDomains.js
|
|
64
56
|
|
|
65
57
|
// Add bellow trusted domains, access tokens will automatically injected to be send to
|
|
66
|
-
// trusted domain can also be a path like https://www.myapi.com/users,
|
|
58
|
+
// trusted domain can also be a path like https://www.myapi.com/users,
|
|
67
59
|
// then all subroute like https://www.myapi.com/useers/1 will be authorized to send access_token to.
|
|
68
60
|
|
|
69
61
|
// Domains used by OIDC server must be also declared here
|
|
70
62
|
const trustedDomains = {
|
|
71
|
-
default:[
|
|
63
|
+
default: ['https://demo.duendesoftware.com', 'https://www.myapi.com/users'],
|
|
72
64
|
};
|
|
73
|
-
|
|
74
65
|
```
|
|
75
66
|
|
|
76
|
-
|
|
67
|
+
## Run The Demo
|
|
77
68
|
|
|
78
69
|
```sh
|
|
79
70
|
git clone https://github.com/AxaGuilDEv/react-oidc.git
|
|
@@ -83,9 +74,9 @@ npm start
|
|
|
83
74
|
# then navigate to http://localhost:4200
|
|
84
75
|
```
|
|
85
76
|
|
|
86
|
-
|
|
77
|
+
## Examples
|
|
87
78
|
|
|
88
|
-
|
|
79
|
+
### Application startup
|
|
89
80
|
|
|
90
81
|
The library is router agnostic and use native History API.
|
|
91
82
|
|
|
@@ -107,26 +98,26 @@ import Routes from './Router';
|
|
|
107
98
|
const configuration = {
|
|
108
99
|
client_id: 'interactive.public.short',
|
|
109
100
|
redirect_uri: window.location.origin + '/authentication/callback',
|
|
110
|
-
silent_redirect_uri:
|
|
101
|
+
silent_redirect_uri:
|
|
102
|
+
window.location.origin + '/authentication/silent-callback',
|
|
111
103
|
scope: 'openid profile email api offline_access', // offline_access scope allow your client to retrieve the refresh_token
|
|
112
104
|
authority: 'https://demo.duendesoftware.com',
|
|
113
|
-
service_worker_relative_url:'/OidcServiceWorker.js',
|
|
114
|
-
service_worker_only:false,
|
|
105
|
+
service_worker_relative_url: '/OidcServiceWorker.js',
|
|
106
|
+
service_worker_only: false,
|
|
115
107
|
};
|
|
116
108
|
|
|
117
109
|
const App = () => (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
110
|
+
<OidcProvider configuration={configuration}>
|
|
111
|
+
<Router>
|
|
112
|
+
<Header />
|
|
113
|
+
<Routes />
|
|
114
|
+
</Router>
|
|
115
|
+
</OidcProvider>
|
|
124
116
|
);
|
|
125
117
|
|
|
126
118
|
render(<App />, document.getElementById('root'));
|
|
127
119
|
```
|
|
128
120
|
|
|
129
|
-
|
|
130
121
|
```javascript
|
|
131
122
|
const propTypes = {
|
|
132
123
|
loadingComponent: PropTypes.elementType, // you can inject your own loading component
|
|
@@ -145,56 +136,84 @@ const propTypes = {
|
|
|
145
136
|
scope: PropTypes.string.isRequired, // oidc scope (you need to set "offline_access")
|
|
146
137
|
authority: PropTypes.string.isRequired,
|
|
147
138
|
storage: Storage, // Default sessionStorage, you can set localStorage but it is less secure to XSS attacks
|
|
148
|
-
authority_configuration: PropTypes.shape({
|
|
139
|
+
authority_configuration: PropTypes.shape({
|
|
140
|
+
// Optional for providers that does not implement OIDC server auto discovery via a .wellknowurl
|
|
149
141
|
authorization_endpoint: PropTypes.string,
|
|
150
142
|
token_endpoint: PropTypes.string,
|
|
151
143
|
userinfo_endpoint: PropTypes.string,
|
|
152
144
|
end_session_endpoint: PropTypes.string,
|
|
153
145
|
revocation_endpoint: PropTypes.string,
|
|
154
|
-
check_session_iframe: PropTypes.string
|
|
146
|
+
check_session_iframe: PropTypes.string,
|
|
155
147
|
}),
|
|
156
148
|
refresh_time_before_tokens_expiration_in_second: PropTypes.number,
|
|
157
149
|
service_worker_relative_url: PropTypes.string,
|
|
158
150
|
service_worker_only: PropTypes.boolean, // default false
|
|
159
|
-
extras: StringMap|undefined, // ex: {'prompt': 'consent', 'access_type': 'offline'} list of key/value that are send to the oidc server (more info: https://github.com/openid/AppAuth-JS)
|
|
160
|
-
withCustomHistory: PropTypes.function, // Override history modification, return instance with replaceState(url, stateHistory) implemented (like History.replaceState())
|
|
161
|
-
authority_time_cache_wellknowurl_in_second: 60* 60, // Time to cache in second of openid wellknowurl, default is 1 hour
|
|
151
|
+
extras: StringMap | undefined, // ex: {'prompt': 'consent', 'access_type': 'offline'} list of key/value that are send to the oidc server (more info: https://github.com/openid/AppAuth-JS)
|
|
152
|
+
withCustomHistory: PropTypes.function, // Override history modification, return instance with replaceState(url, stateHistory) implemented (like History.replaceState())
|
|
153
|
+
authority_time_cache_wellknowurl_in_second: 60 * 60, // Time to cache in second of openid wellknowurl, default is 1 hour
|
|
162
154
|
monitor_session: PropTypes.boolean, // Add OpenId monitor session, default is false (more information https://openid.net/specs/openid-connect-session-1_0.html), if you need to set it to true consider https://infi.nl/nieuws/spa-necromancy/
|
|
163
155
|
onLogoutFromAnotherTab: Function, // Optional, can be set to override the default behavior, this function is triggered when user with the same subject is logged out from another tab when session_monitor is active
|
|
164
156
|
onLogoutFromSameTab: Function, // Optional, can be set to override the default behavior, this function is triggered when user is logged out from same tab when session_monitor is active
|
|
165
|
-
token_renew_mode: PropTypes.string // Optional, update tokens base on the selected token(s) lifetime: "access_token_or_id_token_invalid" (default), "access_token_invalid" , "id_token_invalid"
|
|
166
|
-
}).isRequired
|
|
157
|
+
token_renew_mode: PropTypes.string, // Optional, update tokens base on the selected token(s) lifetime: "access_token_or_id_token_invalid" (default), "access_token_invalid" , "id_token_invalid"
|
|
158
|
+
}).isRequired,
|
|
167
159
|
};
|
|
168
160
|
```
|
|
161
|
+
|
|
169
162
|
## How to consume
|
|
170
163
|
|
|
171
164
|
"useOidc" returns all props from the Hook :
|
|
172
165
|
|
|
173
166
|
```javascript
|
|
174
167
|
import React from 'react';
|
|
175
|
-
import {useOidc} from
|
|
168
|
+
import { useOidc } from './oidc';
|
|
176
169
|
|
|
177
170
|
export const Home = () => {
|
|
171
|
+
const { login, logout, renewTokens, isAuthenticated } = useOidc();
|
|
178
172
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<div className=
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
173
|
+
return (
|
|
174
|
+
<div className='container-fluid mt-3'>
|
|
175
|
+
<div className='card'>
|
|
176
|
+
<div className='card-body'>
|
|
177
|
+
<h5 className='card-title'>Welcome !!!</h5>
|
|
178
|
+
<p className='card-text'>
|
|
179
|
+
React Demo Application protected by OpenId Connect
|
|
180
|
+
</p>
|
|
181
|
+
{!isAuthenticated && (
|
|
182
|
+
<button
|
|
183
|
+
type='button'
|
|
184
|
+
className='btn btn-primary'
|
|
185
|
+
onClick={() => login('/profile')}
|
|
186
|
+
>
|
|
187
|
+
Login
|
|
188
|
+
</button>
|
|
189
|
+
)}
|
|
190
|
+
{isAuthenticated && (
|
|
191
|
+
<button
|
|
192
|
+
type='button'
|
|
193
|
+
className='btn btn-primary'
|
|
194
|
+
onClick={() => logout()}
|
|
195
|
+
>
|
|
196
|
+
logout
|
|
197
|
+
</button>
|
|
198
|
+
)}
|
|
199
|
+
{isAuthenticated && (
|
|
200
|
+
<button
|
|
201
|
+
type='button'
|
|
202
|
+
className='btn btn-primary'
|
|
203
|
+
onClick={() => renewTokens()}
|
|
204
|
+
>
|
|
205
|
+
renewTokens
|
|
206
|
+
</button>
|
|
207
|
+
)}
|
|
192
208
|
</div>
|
|
193
|
-
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
194
212
|
};
|
|
195
|
-
|
|
196
213
|
```
|
|
214
|
+
|
|
197
215
|
The Hook method exposes :
|
|
216
|
+
|
|
198
217
|
- isAuthenticated : if the user is logged in or not
|
|
199
218
|
- logout: logout function (return a promise)
|
|
200
219
|
- login: login function 'return a promise'
|
|
@@ -202,7 +221,7 @@ The Hook method exposes :
|
|
|
202
221
|
|
|
203
222
|
## How to secure a component
|
|
204
223
|
|
|
205
|
-
|
|
224
|
+
`OidcSecure` component trigger authentication in case user is not authenticated. So, the children of that component can be accessible only once you are connected.
|
|
206
225
|
|
|
207
226
|
```javascript
|
|
208
227
|
import React from 'react';
|
|
@@ -232,10 +251,10 @@ import Admin from '../Pages/Admin';
|
|
|
232
251
|
|
|
233
252
|
const Routes = () => (
|
|
234
253
|
<Switch>
|
|
235
|
-
<Route exact path=
|
|
236
|
-
<Route path=
|
|
237
|
-
<Route path=
|
|
238
|
-
<Route path=
|
|
254
|
+
<Route exact path='/' component={Home} />
|
|
255
|
+
<Route path='/dashboard' component={withOidcSecure(Dashboard)} />
|
|
256
|
+
<Route path='/admin' component={Admin} />
|
|
257
|
+
<Route path='/home' component={Home} />
|
|
239
258
|
</Switch>
|
|
240
259
|
);
|
|
241
260
|
|
|
@@ -248,21 +267,28 @@ export default Routes;
|
|
|
248
267
|
import { useOidcAccessToken } from '@axa-fr/react-oidc';
|
|
249
268
|
|
|
250
269
|
const DisplayAccessToken = () => {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
const { accessToken, accessTokenPayload } = useOidcAccessToken();
|
|
271
|
+
|
|
272
|
+
if (!accessToken) {
|
|
273
|
+
return <p>you are not authentified</p>;
|
|
274
|
+
}
|
|
275
|
+
return (
|
|
276
|
+
<div className='card text-white bg-info mb-3'>
|
|
277
|
+
<div className='card-body'>
|
|
278
|
+
<h5 className='card-title'>Access Token</h5>
|
|
279
|
+
<p style={{ color: 'red', backgroundColor: 'white' }}>
|
|
280
|
+
Please consider to configure the ServiceWorker in order to protect
|
|
281
|
+
your application from XSRF attacks. ""access_token" and
|
|
282
|
+
"refresh_token" will never be accessible from your client side
|
|
283
|
+
javascript.
|
|
284
|
+
</p>
|
|
285
|
+
{<p className='card-text'>{JSON.stringify(accessToken)}</p>}
|
|
286
|
+
{accessTokenPayload != null && (
|
|
287
|
+
<p className='card-text'>{JSON.stringify(accessTokenPayload)}</p>
|
|
288
|
+
)}
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
266
292
|
};
|
|
267
293
|
```
|
|
268
294
|
|
|
@@ -271,26 +297,26 @@ const DisplayAccessToken = () => {
|
|
|
271
297
|
```javascript
|
|
272
298
|
import { useOidcIdToken } from '@axa-fr/react-oidc';
|
|
273
299
|
|
|
274
|
-
const DisplayIdToken =() => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if(!idToken){
|
|
278
|
-
return <p>you are not authentified</p>
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return (
|
|
282
|
-
<div className="card text-white bg-info mb-3">
|
|
283
|
-
<div className="card-body">
|
|
284
|
-
<h5 className="card-title">ID Token</h5>
|
|
285
|
-
{<p className="card-text">{JSON.stringify(idToken)}</p>}
|
|
286
|
-
{idTokenPayload != null && <p className="card-text">{JSON.stringify(idTokenPayload)}</p>}
|
|
287
|
-
</div>
|
|
288
|
-
</div>
|
|
289
|
-
);
|
|
290
|
-
}
|
|
300
|
+
const DisplayIdToken = () => {
|
|
301
|
+
const { idToken, idTokenPayload } = useOidcIdToken();
|
|
291
302
|
|
|
292
|
-
|
|
303
|
+
if (!idToken) {
|
|
304
|
+
return <p>you are not authentified</p>;
|
|
305
|
+
}
|
|
293
306
|
|
|
307
|
+
return (
|
|
308
|
+
<div className='card text-white bg-info mb-3'>
|
|
309
|
+
<div className='card-body'>
|
|
310
|
+
<h5 className='card-title'>ID Token</h5>
|
|
311
|
+
{<p className='card-text'>{JSON.stringify(idToken)}</p>}
|
|
312
|
+
{idTokenPayload != null && (
|
|
313
|
+
<p className='card-text'>{JSON.stringify(idTokenPayload)}</p>
|
|
314
|
+
)}
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
```
|
|
294
320
|
|
|
295
321
|
## How to get User Information : Hook method
|
|
296
322
|
|
|
@@ -298,9 +324,9 @@ const DisplayIdToken =() => {
|
|
|
298
324
|
import { useOidcUser, UserStatus } from '@axa-fr/react-oidc';
|
|
299
325
|
|
|
300
326
|
const DisplayUserInfo = () => {
|
|
301
|
-
const{ oidcUser, oidcUserLoadingState } = useOidcUser();
|
|
327
|
+
const { oidcUser, oidcUserLoadingState } = useOidcUser();
|
|
302
328
|
|
|
303
|
-
switch (oidcUserLoadingState){
|
|
329
|
+
switch (oidcUserLoadingState) {
|
|
304
330
|
case UserStatus.Loading:
|
|
305
331
|
return <p>User Information are loading</p>;
|
|
306
332
|
case UserStatus.Unauthenticated:
|
|
@@ -309,16 +335,15 @@ const DisplayUserInfo = () => {
|
|
|
309
335
|
return <p>Fail to load user information</p>;
|
|
310
336
|
default:
|
|
311
337
|
return (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
338
|
+
<div className='card text-white bg-success mb-3'>
|
|
339
|
+
<div className='card-body'>
|
|
340
|
+
<h5 className='card-title'>User information</h5>
|
|
341
|
+
<p className='card-text'>{JSON.stringify(oidcUser)}</p>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
318
344
|
);
|
|
319
345
|
}
|
|
320
346
|
};
|
|
321
|
-
|
|
322
347
|
```
|
|
323
348
|
|
|
324
349
|
## How to get a fetch that inject Access_Token : Hook method
|
|
@@ -327,7 +352,7 @@ If your are not using the service worker. Fetch function need to send AccessToke
|
|
|
327
352
|
This Hook give you a wrapped fetch that add the access token for you.
|
|
328
353
|
|
|
329
354
|
```javascript
|
|
330
|
-
import React, {useEffect, useState} from 'react';
|
|
355
|
+
import React, { useEffect, useState } from 'react';
|
|
331
356
|
import { useOidcFetch, OidcSecure } from '@axa-fr/react-oidc';
|
|
332
357
|
|
|
333
358
|
const DisplayUserInfo = ({ fetch }) => {
|
|
@@ -336,7 +361,9 @@ const DisplayUserInfo = ({ fetch }) => {
|
|
|
336
361
|
|
|
337
362
|
useEffect(() => {
|
|
338
363
|
const fetchUserInfoAsync = async () => {
|
|
339
|
-
const res = await fetch(
|
|
364
|
+
const res = await fetch(
|
|
365
|
+
'https://demo.duendesoftware.com/connect/userinfo'
|
|
366
|
+
);
|
|
340
367
|
if (res.status != 200) {
|
|
341
368
|
return null;
|
|
342
369
|
}
|
|
@@ -344,37 +371,42 @@ const DisplayUserInfo = ({ fetch }) => {
|
|
|
344
371
|
};
|
|
345
372
|
let isMounted = true;
|
|
346
373
|
fetchUserInfoAsync().then((userInfo) => {
|
|
347
|
-
if(isMounted) {
|
|
374
|
+
if (isMounted) {
|
|
348
375
|
setLoading(false);
|
|
349
|
-
setOidcUser(userInfo)
|
|
376
|
+
setOidcUser(userInfo);
|
|
350
377
|
}
|
|
351
|
-
})
|
|
352
|
-
return
|
|
378
|
+
});
|
|
379
|
+
return () => {
|
|
353
380
|
isMounted = false;
|
|
354
381
|
};
|
|
355
|
-
},[]);
|
|
382
|
+
}, []);
|
|
356
383
|
|
|
357
|
-
if(isLoading){
|
|
384
|
+
if (isLoading) {
|
|
358
385
|
return <>Loading</>;
|
|
359
386
|
}
|
|
360
387
|
|
|
361
388
|
return (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
389
|
+
<div className='container mt-3'>
|
|
390
|
+
<div className='card text-white bg-success mb-3'>
|
|
391
|
+
<div className='card-body'>
|
|
392
|
+
<h5 className='card-title'>User information</h5>
|
|
393
|
+
{oidcUser != null && (
|
|
394
|
+
<p className='card-text'>{JSON.stringify(oidcUser)}</p>
|
|
395
|
+
)}
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
371
400
|
};
|
|
372
401
|
|
|
373
|
-
export const FetchUserHook= () => {
|
|
374
|
-
const {fetch} = useOidcFetch();
|
|
375
|
-
return
|
|
376
|
-
|
|
377
|
-
|
|
402
|
+
export const FetchUserHook = () => {
|
|
403
|
+
const { fetch } = useOidcFetch();
|
|
404
|
+
return (
|
|
405
|
+
<OidcSecure>
|
|
406
|
+
<DisplayUserInfo fetch={fetch} />
|
|
407
|
+
</OidcSecure>
|
|
408
|
+
);
|
|
409
|
+
};
|
|
378
410
|
```
|
|
379
411
|
|
|
380
412
|
## How to get a fetch that inject Access_Token : HOC method
|
|
@@ -383,7 +415,7 @@ If your are not using the service worker. Fetch function need to send AccessToke
|
|
|
383
415
|
This HOC give you a wrapped fetch that add the access token for you.
|
|
384
416
|
|
|
385
417
|
```javascript
|
|
386
|
-
import React, {useEffect, useState} from 'react';
|
|
418
|
+
import React, { useEffect, useState } from 'react';
|
|
387
419
|
import { useOidcFetch, OidcSecure } from '@axa-fr/react-oidc';
|
|
388
420
|
|
|
389
421
|
const DisplayUserInfo = ({ fetch }) => {
|
|
@@ -392,7 +424,9 @@ const DisplayUserInfo = ({ fetch }) => {
|
|
|
392
424
|
|
|
393
425
|
useEffect(() => {
|
|
394
426
|
const fetchUserInfoAsync = async () => {
|
|
395
|
-
const res = await fetch(
|
|
427
|
+
const res = await fetch(
|
|
428
|
+
'https://demo.duendesoftware.com/connect/userinfo'
|
|
429
|
+
);
|
|
396
430
|
if (res.status != 200) {
|
|
397
431
|
return null;
|
|
398
432
|
}
|
|
@@ -400,45 +434,48 @@ const DisplayUserInfo = ({ fetch }) => {
|
|
|
400
434
|
};
|
|
401
435
|
let isMounted = true;
|
|
402
436
|
fetchUserInfoAsync().then((userInfo) => {
|
|
403
|
-
if(isMounted) {
|
|
437
|
+
if (isMounted) {
|
|
404
438
|
setLoading(false);
|
|
405
|
-
setOidcUser(userInfo)
|
|
439
|
+
setOidcUser(userInfo);
|
|
406
440
|
}
|
|
407
|
-
})
|
|
408
|
-
return
|
|
441
|
+
});
|
|
442
|
+
return () => {
|
|
409
443
|
isMounted = false;
|
|
410
444
|
};
|
|
411
|
-
},[]);
|
|
445
|
+
}, []);
|
|
412
446
|
|
|
413
|
-
if(isLoading){
|
|
447
|
+
if (isLoading) {
|
|
414
448
|
return <>Loading</>;
|
|
415
449
|
}
|
|
416
450
|
|
|
417
451
|
return (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
452
|
+
<div className='container mt-3'>
|
|
453
|
+
<div className='card text-white bg-success mb-3'>
|
|
454
|
+
<div className='card-body'>
|
|
455
|
+
<h5 className='card-title'>User information</h5>
|
|
456
|
+
{oidcUser != null && (
|
|
457
|
+
<p className='card-text'>{JSON.stringify(oidcUser)}</p>
|
|
458
|
+
)}
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
);
|
|
427
463
|
};
|
|
428
464
|
|
|
429
465
|
const UserInfoWithFetchHoc = withOidcFetch(fetch)(DisplayUserInfo);
|
|
430
|
-
export const FetchUserHoc= () =>
|
|
431
|
-
|
|
466
|
+
export const FetchUserHoc = () => (
|
|
467
|
+
<OidcSecure>
|
|
468
|
+
<UserInfoWithFetchHoc />
|
|
469
|
+
</OidcSecure>
|
|
470
|
+
);
|
|
432
471
|
```
|
|
433
472
|
|
|
434
|
-
|
|
435
473
|
## Components override
|
|
436
474
|
|
|
437
475
|
You can inject your own components.
|
|
438
|
-
All components definition receive props
|
|
476
|
+
All components definition receive props `configurationName`. Please checkout the demo for more complete example.
|
|
439
477
|
|
|
440
478
|
```javascript
|
|
441
|
-
|
|
442
479
|
import React from 'react';
|
|
443
480
|
import { render } from 'react-dom';
|
|
444
481
|
import { BrowserRouter as Router } from 'react-router-dom';
|
|
@@ -455,16 +492,16 @@ const configuration = {
|
|
|
455
492
|
silent_redirect_uri: 'http://localhost:4200/authentication/silent-callback',
|
|
456
493
|
scope: 'openid profile email api offline_access',
|
|
457
494
|
authority: 'https://demo.identityserver.io',
|
|
458
|
-
service_worker_relative_url:'/OidcServiceWorker.js',
|
|
459
|
-
service_worker_only:false,
|
|
495
|
+
service_worker_relative_url: '/OidcServiceWorker.js',
|
|
496
|
+
service_worker_only: false,
|
|
460
497
|
};
|
|
461
498
|
|
|
462
|
-
const Loading = () => <p>Loading</p
|
|
463
|
-
const AuthenticatingError = () => <p>Authenticating error</p
|
|
464
|
-
const Authenticating = () => <p>Authenticating</p
|
|
465
|
-
const SessionLost = () => <p>Session Lost</p
|
|
466
|
-
const ServiceWorkerNotSupported = () => <p>Not supported</p
|
|
467
|
-
const CallBackSuccess = () => <p>Success</p
|
|
499
|
+
const Loading = () => <p>Loading</p>;
|
|
500
|
+
const AuthenticatingError = () => <p>Authenticating error</p>;
|
|
501
|
+
const Authenticating = () => <p>Authenticating</p>;
|
|
502
|
+
const SessionLost = () => <p>Session Lost</p>;
|
|
503
|
+
const ServiceWorkerNotSupported = () => <p>Not supported</p>;
|
|
504
|
+
const CallBackSuccess = () => <p>Success</p>;
|
|
468
505
|
|
|
469
506
|
//const [isSessionLost, setIsSessionLost] = useState(false);
|
|
470
507
|
|
|
@@ -473,109 +510,113 @@ const CallBackSuccess = () => <p>Success</p>
|
|
|
473
510
|
//}
|
|
474
511
|
|
|
475
512
|
const App = () => (
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
513
|
+
<OidcProvider
|
|
514
|
+
configuration={configuration}
|
|
515
|
+
loadingComponent={Loading}
|
|
516
|
+
authenticatingErrorComponent={AuthenticatingError}
|
|
517
|
+
authenticatingComponent={Authenticating}
|
|
518
|
+
sessionLostComponent={SessionLost}
|
|
519
|
+
//onSessionLost={onSessionLost} // If set "sessionLostComponent" is not displayed and onSessionLost callback is called instead
|
|
520
|
+
serviceWorkerNotSupportedComponent={ServiceWorkerNotSupported}
|
|
521
|
+
callbackSuccessComponent={CallBackSuccess}
|
|
522
|
+
>
|
|
523
|
+
{/* isSessionLost && <SessionLost />*/}
|
|
524
|
+
<Router>
|
|
525
|
+
<Header />
|
|
526
|
+
<Routes />
|
|
527
|
+
</Router>
|
|
528
|
+
</OidcProvider>
|
|
491
529
|
);
|
|
492
530
|
|
|
493
531
|
render(<App />, document.getElementById('root'));
|
|
494
|
-
|
|
495
532
|
```
|
|
496
533
|
|
|
497
|
-
|
|
534
|
+
## How It Works
|
|
498
535
|
|
|
499
|
-
These components encapsulate the use of
|
|
536
|
+
These components encapsulate the use of [AppAuth-JS](https://github.com/openid/AppAuth-JS) in order to hide workflow complexity.
|
|
500
537
|
Internally, native History API is used to be router library agnostic.
|
|
501
|
-
It use AppAuthJS behind the scene because it very lightweight and created by
|
|
538
|
+
It use AppAuthJS behind the scene because it very lightweight and created by OpenID certification team. `oidc-client` used in V3 was heavy and no longer maintained.
|
|
502
539
|
|
|
503
540
|
More information about OIDC
|
|
504
|
-
- French: https://medium.com/just-tech-it-now/augmentez-la-s%C3%A9curit%C3%A9-et-la-simplicit%C3%A9-de-votre-syst%C3%A8me-dinformation-avec-oauth-2-0-cf0732d71284
|
|
505
|
-
- English: https://medium.com/just-tech-it-now/increase-the-security-and-simplicity-of-your-information-system-with-openid-connect-fa8c26b99d6d
|
|
506
541
|
|
|
542
|
+
- [French](https://medium.com/just-tech-it-now/augmentez-la-s%C3%A9curit%C3%A9-et-la-simplicit%C3%A9-de-votre-syst%C3%A8me-dinformation-avec-oauth-2-0-cf0732d71284)
|
|
543
|
+
- [English](https://medium.com/just-tech-it-now/increase-the-security-and-simplicity-of-your-information-system-with-openid-connect-fa8c26b99d6d)
|
|
507
544
|
|
|
508
|
-
|
|
509
|
-
# NextJS
|
|
545
|
+
## NextJS
|
|
510
546
|
|
|
511
547
|
To work with NextJS you need to inject your own history surcharge like the sample below.
|
|
512
548
|
|
|
513
|
-
component/layout.js
|
|
549
|
+
**component/layout.js**
|
|
550
|
+
|
|
514
551
|
```javascript
|
|
515
552
|
import { OidcProvider } from '@axa-fr/react-oidc';
|
|
516
|
-
import { useRouter } from 'next/router'
|
|
553
|
+
import { useRouter } from 'next/router';
|
|
517
554
|
|
|
518
555
|
const configuration = {
|
|
519
556
|
client_id: 'interactive.public.short',
|
|
520
557
|
redirect_uri: 'http://localhost:3001/#authentication/callback',
|
|
521
558
|
silent_redirect_uri: 'http://localhost:3001/#authentication/silent-callback', // Optional activate silent-login that use cookies between OIDC server and client javascript to restore the session
|
|
522
559
|
scope: 'openid profile email api offline_access',
|
|
523
|
-
authority: 'https://demo.duendesoftware.com'
|
|
560
|
+
authority: 'https://demo.duendesoftware.com',
|
|
524
561
|
};
|
|
525
562
|
|
|
526
|
-
const onEvent=(configurationName, eventName, data
|
|
563
|
+
const onEvent = (configurationName, eventName, data) => {
|
|
527
564
|
console.log(`oidc:${configurationName}:${eventName}`, data);
|
|
528
|
-
}
|
|
565
|
+
};
|
|
529
566
|
|
|
530
567
|
export default function Layout({ children }) {
|
|
531
|
-
const router = useRouter()
|
|
532
|
-
const withCustomHistory= () => {
|
|
568
|
+
const router = useRouter();
|
|
569
|
+
const withCustomHistory = () => {
|
|
533
570
|
return {
|
|
534
571
|
replaceState: (url) => {
|
|
535
|
-
router
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
572
|
+
router
|
|
573
|
+
.replace({
|
|
574
|
+
pathname: url,
|
|
575
|
+
})
|
|
576
|
+
.then(() => {
|
|
577
|
+
window.dispatchEvent(new Event('popstate'));
|
|
578
|
+
});
|
|
579
|
+
},
|
|
541
580
|
};
|
|
542
581
|
};
|
|
543
|
-
|
|
582
|
+
|
|
544
583
|
return (
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
584
|
+
<>
|
|
585
|
+
<OidcProvider
|
|
586
|
+
configuration={configuration}
|
|
587
|
+
onEvent={onEvent}
|
|
588
|
+
withCustomHistory={withCustomHistory}
|
|
589
|
+
>
|
|
590
|
+
<main>{children}</main>
|
|
591
|
+
</OidcProvider>
|
|
592
|
+
</>
|
|
593
|
+
);
|
|
551
594
|
}
|
|
552
|
-
|
|
553
595
|
```
|
|
554
596
|
|
|
555
|
-
For more information checkout the
|
|
597
|
+
For more information checkout the [NextJS React OIDC demo](https://github.com/AxaGuilDEv/react-oidc/tree/master/packages/nextjs-demo)
|
|
556
598
|
|
|
557
|
-
|
|
599
|
+
## Hash route
|
|
558
600
|
|
|
559
|
-
|
|
601
|
+
`react-oidc` work also with hash router.
|
|
560
602
|
|
|
561
603
|
```javascript
|
|
562
604
|
export const configurationIdentityServerWithHash = {
|
|
563
|
-
client_id: 'interactive.public.short',
|
|
564
|
-
redirect_uri: window.location.origin+'#authentication-callback',
|
|
565
|
-
silent_redirect_uri:
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
605
|
+
client_id: 'interactive.public.short',
|
|
606
|
+
redirect_uri: window.location.origin + '#authentication-callback',
|
|
607
|
+
silent_redirect_uri:
|
|
608
|
+
window.location.origin + '#authentication-silent-callback',
|
|
609
|
+
scope: 'openid profile email api offline_access',
|
|
610
|
+
authority: 'https://demo.duendesoftware.com',
|
|
611
|
+
refresh_time_before_tokens_expiration_in_second: 70,
|
|
612
|
+
service_worker_relative_url: '/OidcServiceWorker.js',
|
|
613
|
+
service_worker_only: false,
|
|
571
614
|
};
|
|
572
|
-
|
|
573
|
-
|
|
574
615
|
```
|
|
575
616
|
|
|
576
|
-
|
|
617
|
+
## Service Worker Support
|
|
577
618
|
|
|
578
|
-
- Firefox : tested on
|
|
619
|
+
- Firefox : tested on Firefox 98.0.2
|
|
579
620
|
- Chrome/Edge : tested on version upper to 90
|
|
580
621
|
- Opera : tested on version upper to 80
|
|
581
622
|
- Safari : tested on Safari/605.1.15
|