Реализация OIDC
This commit is contained in:
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
||||
HTTPS=true
|
||||
SSL_CRT_FILE=./certs/cert.pem
|
||||
SSL_KEY_FILE=./certs/key.pem
|
||||
23
certs/cert.pem
Normal file
23
certs/cert.pem
Normal file
@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDwTCCAqmgAwIBAgIUAr54kkXIb/FCWQ1zQG+QOXtfEu0wDQYJKoZIhvcNAQEL
|
||||
BQAwcDELMAkGA1UEBhMCUlUxDTALBgNVBAgMBFBlcm0xDjAMBgNVBAcMBUNoYWlr
|
||||
MQwwCgYDVQQKDANJYW0xDTALBgNVBAMMBEVnb3IxJTAjBgkqhkiG9w0BCQEWFmVn
|
||||
b3IubXVyYXRvdkBnbWFpbC5jb20wHhcNMjUwMjI4MTY0MDA1WhcNMjYwMjI4MTY0
|
||||
MDA1WjBwMQswCQYDVQQGEwJSVTENMAsGA1UECAwEUGVybTEOMAwGA1UEBwwFQ2hh
|
||||
aWsxDDAKBgNVBAoMA0lhbTENMAsGA1UEAwwERWdvcjElMCMGCSqGSIb3DQEJARYW
|
||||
ZWdvci5tdXJhdG92QGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAK/EdtIakt3x8/vjvDveFo6eugk3GSRZNcnDb0HBTCDOpbkCxi3NcdBZ
|
||||
glr3iSEOR2pn/9PhlKO6wR3HoBDy9viAE/CkLkBE2VGYRuSIitozYOONqXbAIQJm
|
||||
gLGpuSMANrx3idBLb76UnZGgG62DdtYIuH7OUgF96twIYnKrsZAO2GtZ/Vr+c8U7
|
||||
7x9P1HQZxtcZzOPTcTQW+dEstSer9tjJYAmTi1gOdu6WQZKUAHMiiE5Q0tzjDhkk
|
||||
Xg7X3ttsnWUIgOPYgu7gF4NCdF7JhtuLroSTBpkU/xxwUrkmlcS/TmLAy9Lsmz9Q
|
||||
+UBu6XCnWRj4lAufV6SaDFqvkfQeWa8CAwEAAaNTMFEwHQYDVR0OBBYEFKfxOTgB
|
||||
vdvXzHq4xBxWeYfn77NNMB8GA1UdIwQYMBaAFKfxOTgBvdvXzHq4xBxWeYfn77NN
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACvaRSOwF+ib5Vpn
|
||||
2weYkIzXSDevbuf+EGlNZnxTBXtBYbhXvjK4uGCdf3uQiBkanwOTyUHFc6OtV1Io
|
||||
eph11Z+dPrLmZOJ24TT2dD1C70CBROTwH/z5om6I/JPRRd9sBzIs+Dr78uSJ8mN3
|
||||
NJb6E+hP5lcgqsk0N0v3Ln3bBOm5nFVfcSrNNjccUAnp+OrJMe2gU3Otz0YE2zOm
|
||||
nkkf9vHE+IADTDUY5M978DYA90PF1oTAHMdvGZYWNUGi4zCbubzI40Wq2gLG5A2h
|
||||
k9FAyM+4AbVxUoYxdFVeBLdCHb594KcOj1O0ujbFN3mQ96pMQJAv/2Cl8vLkqNx1
|
||||
5pWa0d0=
|
||||
-----END CERTIFICATE-----
|
||||
28
certs/key.pem
Normal file
28
certs/key.pem
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvxHbSGpLd8fP7
|
||||
47w73haOnroJNxkkWTXJw29BwUwgzqW5AsYtzXHQWYJa94khDkdqZ//T4ZSjusEd
|
||||
x6AQ8vb4gBPwpC5ARNlRmEbkiIraM2Djjal2wCECZoCxqbkjADa8d4nQS2++lJ2R
|
||||
oButg3bWCLh+zlIBfercCGJyq7GQDthrWf1a/nPFO+8fT9R0GcbXGczj03E0FvnR
|
||||
LLUnq/bYyWAJk4tYDnbulkGSlABzIohOUNLc4w4ZJF4O197bbJ1lCIDj2ILu4BeD
|
||||
QnReyYbbi66EkwaZFP8ccFK5JpXEv05iwMvS7Js/UPlAbulwp1kY+JQLn1ekmgxa
|
||||
r5H0HlmvAgMBAAECggEAQltjmHKb29rKh+A0Yk24KmPWTEBW55gebGuyBxsYyJH7
|
||||
kttvQj97pnMEeZ9WT/p6D7vvo2hYm2+YFMwWrA9uGecQoBr7sxvLB7j7mq/J7BLV
|
||||
k1MaFVD3pVZZY7l1wbcE8yYWC2NPbp3g1uehS2KEbM2iCY2O/C0zi5pGwI/9Wyr/
|
||||
ZlHfE/6b7ttrjOqVxXSnR2MN4VZI+0ixpG2ojqQmaWTYyArFmHb8CDl1LIa2mfJQ
|
||||
m628GGYdHFKqsKOtnjpAyer0OTGJNRL9mjN/Y0jlsD3zTAOR/j48A9c6FmY1ktam
|
||||
m0lX4/ZcZnEjrEObODLIxaaizDTryOTXrqVBJ0uREQKBgQDZaRsd8W/+eMAuTJEM
|
||||
qVAduxbiw87T8a6NXEcAiEF5vhxqpXGh+FR4ufbkr41wHL21IYVGQQU+M3VcgGp2
|
||||
bh8CyGk1olrl2VIExjcA2QR8dJ/YmxzKb2Ja6ZRMuG3VufZYxqtbcbJniap2GueC
|
||||
C8iykeGTaz7MOUXJm9q6JFVHHQKBgQDO9yQEyUnBn0OrYZdqB3pAdjDCJB0Xav3H
|
||||
uWu/i7qlBtveeWOt8y7NkC0UG5URlSXejncUmwQPEfMOhIg0Er/c/rZdpp9hoBNa
|
||||
eH8MusNQIKZPXOGtBwikIsSJeQ04t1NXzSDEUe6lZER3DAq7BMocyXCEpITMwC/a
|
||||
0+NEvKLuOwKBgQC9md4eNOqYoDHprrhotFe8Neb1iBId4A18FleNbUa8p0Ec+H+q
|
||||
42i3iGZ6dWcBuO7wwfT6mcW6wyG8s/kko1DEGoc2UQq4nNfcdgiN4rT43LRyMIPh
|
||||
P1YlNsMwTT7sPytJrKjQLM2LYhGYwknXrfMvV+3DpKm1bNUhx5vu7bS5OQKBgAMe
|
||||
vrBDyJTercp7oii7DCDEp1+F49pihojoRrOQi7PJMq9b7SDGNcJrlgJjmA+3y+Zb
|
||||
B3iMDbeccamaXeNLFRFj1aP7yxNRsnj+sAulFSS0GU3A/LX7ESpIS+Y2qPhd6ye7
|
||||
s+7BvXNI269fwxmmrNVaRBP71vSvQQlvgFGc9mfZAoGAAzV+Qs2SUT8FtoBOUbMD
|
||||
APCMrraaNQtPKpTq/KCKHK5FsU09RMK7EQC7XcdqKMZqOeLFyBRqVEc8WOqbdmyy
|
||||
A9Ed7EVKpmDRhbr1ApmH0AQW38lYFguDluf5cWQLhrAntTmSAJdkJJgWw/EKR5zY
|
||||
hmuxletwhzNRsV8CrJjNrLc=
|
||||
-----END PRIVATE KEY-----
|
||||
82
package-lock.json
generated
82
package-lock.json
generated
@ -12,8 +12,10 @@
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
}
|
||||
@ -3418,6 +3420,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
||||
@ -10207,6 +10214,14 @@
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -10900,6 +10915,17 @@
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"node_modules/oidc-client-ts": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.1.0.tgz",
|
||||
"integrity": "sha512-IDopEXjiwjkmJLYZo6BTlvwOtnlSniWZkKZoXforC/oLZHC9wkIxd25Kwtmo5yKFMMVcsp3JY6bhcNJqdYk8+g==",
|
||||
"dependencies": {
|
||||
"jwt-decode": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@ -12883,6 +12909,52 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.2.0.tgz",
|
||||
"integrity": "sha512-fXyqzPgCPZbqhrk7k3hPcCpYIlQ2ugIXDboHUzhJISFVy2DEPsmHgN588MyGmkIOv3jDgNfUE3kJi83L28s/LQ==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"turbo-stream": "2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.2.0.tgz",
|
||||
"integrity": "sha512-cU7lTxETGtQRQbafJubvZKHEn5izNABxZhBY0Jlzdv0gqQhCPQt2J8aN5ZPjS6mQOXn5NnirWNh+FpE8TTYN0Q==",
|
||||
"dependencies": {
|
||||
"react-router": "7.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router/node_modules/cookie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-scripts": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
||||
@ -13728,6 +13800,11 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
@ -14992,6 +15069,11 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/turbo-stream": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
|
||||
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g=="
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
||||
@ -7,8 +7,10 @@
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
|
||||
37
src/App.js
37
src/App.js
@ -1,25 +1,22 @@
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
// src/App.js
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Login from './components/Login';
|
||||
import Logout from './components/Logout';
|
||||
import Callback from './components/Callback';
|
||||
import Home from './components/Home';
|
||||
|
||||
function App() {
|
||||
const App = () => {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/logout" element={<Logout />} />
|
||||
<Route path="/callback" element={<Callback />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
20
src/components/Callback.js
Normal file
20
src/components/Callback.js
Normal file
@ -0,0 +1,20 @@
|
||||
// src/Callback.js
|
||||
import React, { useEffect } from 'react';
|
||||
import { userManager } from '../config/authConfig';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const Callback = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
userManager.signinRedirectCallback().then(() => {
|
||||
navigate('/'); // Перенаправление на главную страницу после успешной аутентификации
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, [navigate]);
|
||||
|
||||
return <div>Loading...</div>;
|
||||
};
|
||||
|
||||
export default Callback;
|
||||
31
src/components/Home.js
Normal file
31
src/components/Home.js
Normal file
@ -0,0 +1,31 @@
|
||||
// src/Home.js
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { userManager } from '../config/authConfig';
|
||||
|
||||
const Home = () => {
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
userManager.getUser().then((user) => {
|
||||
if (user) {
|
||||
setUser(user);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!user) {
|
||||
return <div>Please <a href="/login">login</a> to continue.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome, {user.profile.name}</h1>
|
||||
<p>Email: {user.profile.email}</p>
|
||||
<h2>Token Information</h2>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
18
src/components/Login.js
Normal file
18
src/components/Login.js
Normal file
@ -0,0 +1,18 @@
|
||||
// src/Login.js
|
||||
import React from 'react';
|
||||
import { userManager } from '../config/authConfig';
|
||||
|
||||
const Login = () => {
|
||||
const handleLogin = () => {
|
||||
userManager.signinRedirect();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
<button onClick={handleLogin}>Login</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
18
src/components/Logout.js
Normal file
18
src/components/Logout.js
Normal file
@ -0,0 +1,18 @@
|
||||
// src/Logout.js
|
||||
import React from 'react';
|
||||
import { userManager } from '../config/authConfig';
|
||||
|
||||
const Logout = () => {
|
||||
const handleLogout = () => {
|
||||
userManager.signoutRedirect();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Logout</h1>
|
||||
<button onClick={handleLogout}>Logout</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logout;
|
||||
16
src/config/authConfig.js
Normal file
16
src/config/authConfig.js
Normal file
@ -0,0 +1,16 @@
|
||||
// src/authConfig.js
|
||||
import { UserManager, WebStorageStateStore } from 'oidc-client-ts';
|
||||
|
||||
const config = {
|
||||
authority: 'https://localhost:5001/', // Замените на URL вашего OIDC провайдера
|
||||
client_id: 'test-web-oidc', // Замените на ваш Client ID
|
||||
client_secret: 'test-web-oidc',
|
||||
redirect_uri: 'https://localhost:3000/callback', // URL для перенаправления после аутентификации
|
||||
response_type: 'code',
|
||||
scope: 'openid profile email', // Запрошенные scope
|
||||
post_logout_redirect_uri: 'https://localhost:3000/', // URL для перенаправления после выхода
|
||||
userStore: new WebStorageStateStore({ store: window.localStorage }), // Хранение состояния в localStorage
|
||||
loadUserInfo: true, // Загружать ли информацию о пользователе
|
||||
};
|
||||
|
||||
export const userManager = new UserManager(config);
|
||||
Reference in New Issue
Block a user