├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── App.test.js ├── authConfig.js ├── components ├── Callback.js └── Login.js ├── index.css ├── index.js ├── reportWebVitals.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | Please follow the detailed instructions on how to run this React sample with ZITADEL [here](https://zitadel.com/docs/guides/start/quickstart). 4 | 5 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 6 | 7 | ## Available Scripts 8 | 9 | In the project directory, you can run: 10 | 11 | ### `npm start` 12 | 13 | Runs the app in the development mode.\ 14 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 15 | 16 | The page will reload when you make changes.\ 17 | You may also see any lint errors in the console. 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "oidc-client-ts": "^2.2.0", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-router-dom": "^6.6.1", 13 | "react-scripts": "5.0.1", 14 | "stream": "^0.0.2", 15 | "util": "^0.12.5", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/react-user-authentication/fb2d8dd09a0d03c548808e2c5004c578bbe3b24f/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/react-user-authentication/fb2d8dd09a0d03c548808e2c5004c578bbe3b24f/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/react-user-authentication/fb2d8dd09a0d03c548808e2c5004c578bbe3b24f/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 3 | import Login from "./components/Login"; 4 | import Callback from "./components/Callback"; 5 | import authConfig from "./authConfig"; 6 | import { UserManager, WebStorageStateStore } from "oidc-client-ts"; 7 | 8 | function App() { 9 | const userManager = new UserManager({ 10 | userStore: new WebStorageStateStore({ store: window.localStorage }), 11 | ...authConfig, 12 | }); 13 | 14 | function authorize() { 15 | userManager.signinRedirect({ state: "a2123a67ff11413fa19217a9ea0fbad5" }); 16 | } 17 | 18 | function clearAuth() { 19 | userManager.signoutRedirect(); 20 | } 21 | 22 | const [authenticated, setAuthenticated] = useState(null); 23 | const [userInfo, setUserInfo] = useState(null); 24 | 25 | useEffect(() => { 26 | userManager.getUser().then((user) => { 27 | if (user) { 28 | setAuthenticated(true); 29 | } else { 30 | setAuthenticated(false); 31 | } 32 | }); 33 | }, [userManager]); 34 | 35 | return ( 36 | 37 | 38 | } 41 | /> 42 | 53 | } 54 | /> 55 | 56 | 57 | ); 58 | } 59 | 60 | export default App; 61 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/authConfig.js: -------------------------------------------------------------------------------- 1 | const authConfig = { 2 | authority: 'https://some_text.zitadel.cloud/', //Replace with your issuer URL 3 | client_id: 'ABC123@Project', //Replace with your client id 4 | redirect_uri: 'http://localhost:3000/callback', 5 | response_type: 'code', 6 | scope: 'openid profile email', 7 | post_logout_redirect_uri: 'http://localhost:3000/', 8 | userinfo_endpoint: 'https://instance-some_text.zitadel.cloud/oidc/v1/userinfo', //Replace with your user-info endpoint 9 | response_mode: 'query', 10 | code_challenge_method: 'S256', 11 | }; 12 | 13 | export default authConfig; 14 | -------------------------------------------------------------------------------- /src/components/Callback.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import authConfig from '../authConfig'; 3 | 4 | const Callback = ({ auth, setAuth, userManager, userInfo, setUserInfo, handleLogout }) => { 5 | 6 | useEffect(() => { 7 | if (auth === null) { 8 | userManager.signinRedirectCallback().then((user) => { 9 | if (user) { 10 | setAuth(true); 11 | const access_token = user.access_token; 12 | // Make a request to the user info endpoint using the access token 13 | fetch(authConfig.userinfo_endpoint, { 14 | headers: { 15 | 'Authorization': `Bearer ${access_token}` 16 | } 17 | }) 18 | .then(response => response.json()) 19 | .then(userInfo => { 20 | setUserInfo(userInfo); 21 | }); 22 | } else { 23 | setAuth(false); 24 | } 25 | }).catch((error) => { 26 | setAuth(false); 27 | }); 28 | } 29 | }, [auth, userManager, setAuth]); 30 | 31 | 32 | if (auth === true && userInfo) { 33 | return ( 34 |
35 |

Welcome, {userInfo.name}!

36 |

Your ZITADEL Profile Information

37 |

Name: {userInfo.name}

38 |

Email: {userInfo.email}

39 |

Email Verified: {userInfo.email_verified? "Yes": "No"}

40 |

Locale: {userInfo.locale}

41 | 42 | 43 |
44 | ); 45 | } 46 | else { 47 | return
Loading...
; 48 | } 49 | 50 | }; 51 | 52 | export default Callback; 53 | 54 | -------------------------------------------------------------------------------- /src/components/Login.js: -------------------------------------------------------------------------------- 1 | import { Navigate } from "react-router-dom"; 2 | 3 | const Login = ({ auth, handleLogin, userManager }) => { 4 | return ( 5 |
6 | {auth === null &&
Loading...
} 7 | {auth === false && ( 8 |
9 |

Welcome!

10 | 18 |
19 | )} 20 | {auth && } 21 |
22 | ); 23 | }; 24 | 25 | export default Login; 26 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './style.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | /* The body element covers the entire page, so setting the background color here 2 | will set the background color for the whole page */ 3 | body { 4 | font-family: 'Open Sans', sans-serif; 5 | /* The hex code for the light blue color from the image is #9fd3e0 */ 6 | background-color: #9fd3e0; 7 | /* Center the text in the body element */ 8 | text-align: center; 9 | } 10 | 11 | /* The h1, h2, and h3 elements are used for headings */ 12 | h1, h2, h3 { 13 | /* The hex code for the dark blue color from the image is #2c3e50 */ 14 | color: #2c3e50; 15 | text-align: center; 16 | } 17 | 18 | /* The button element represents a clickable button */ 19 | button { 20 | /* The hex code for the light purple color from the image is #9b59b6 */ 21 | background-color: #9b59b6; 22 | /* The hex code for the white color is #ffffff */ 23 | color: #ffffff; 24 | /* Add some padding and a border to the button */ 25 | padding: 10px 20px; 26 | border: none; 27 | /* Add some hover effect to the button */ 28 | cursor: pointer; 29 | } 30 | 31 | button:hover { 32 | /* The hex code for the dark purple color from the image is #8e44ad */ 33 | background-color: #8e44ad; 34 | } 35 | --------------------------------------------------------------------------------