Create a public OpenID Connect client with client id demo-client.
- Standard flow enabled
- Root URL: e.g. http://localhost:5555
- Valid Redirect URIs: /*
- Web Origins: +
Create a public OpenID Connect client with client id demo-client.
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Keycloak SPA Demo</title> | |
| <style> | |
| body { | |
| background-color: #eaeaea; | |
| font-family: sans-serif; | |
| font-size: 10px; | |
| } | |
| button { | |
| font-family: sans-serif; | |
| font-size: 25px; | |
| width: 200px; | |
| background-color: #0085cf; | |
| background-image: linear-gradient(to bottom, #00a8e1 0%, #0085cf 100%); | |
| background-repeat: repeat-x; | |
| border: 2px solid #ccc; | |
| color: #fff; | |
| text-transform: uppercase; | |
| -webkit-box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.5); | |
| -moz-box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.5); | |
| box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.5); | |
| } | |
| button:hover { | |
| background-color: #006ba6; | |
| background-image: none; | |
| -webkit-box-shadow: none; | |
| -moz-box-shadow: none; | |
| box-shadow: none; | |
| } | |
| hr { | |
| border: none; | |
| background-color: #eee; | |
| height: 10px; | |
| } | |
| .menu { | |
| padding: 10px; | |
| margin-bottom: 10px; | |
| } | |
| .content { | |
| font-size: 20px; | |
| background-color: #eee; | |
| border: 1px solid #ccc; | |
| padding: 10px; | |
| -webkit-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.5); | |
| -moz-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.5); | |
| box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.5); | |
| } | |
| .message-content { | |
| font-size: 20px; | |
| padding: 10px; | |
| background-color: #fff; | |
| border: 1px solid #ccc; | |
| } | |
| .token-content { | |
| font-size: 20px; | |
| padding: 5px; | |
| white-space: pre; | |
| text-transform: none; | |
| } | |
| .wrapper { | |
| position: absolute; | |
| left: 10px; | |
| top: 40px; | |
| bottom: 10px; | |
| right: 10px; | |
| } | |
| .error { | |
| color: #a21e22; | |
| } | |
| table { | |
| width: 100%; | |
| } | |
| table.credentials, | |
| table.profile, | |
| table.apps { | |
| width: unset; | |
| } | |
| tr.even { | |
| background-color: #eee; | |
| } | |
| td { | |
| padding: 5px; | |
| } | |
| td.label { | |
| font-weight: bold; | |
| width: 250px; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div> | |
| <h1>ClientId: <span id="clientInfo"></span></h1> | |
| </div> | |
| <div id="welcome" class="wrapper hidden"> | |
| <div class="menu"> | |
| <button name="loginBtn" onclick="keycloak.login()">Login</button> | |
| </div> | |
| <div class="message-content"> | |
| <div class="message">Please login</div> | |
| </div> | |
| </div> | |
| <div id="content" class="wrapper hidden"> | |
| <div class="menu"> | |
| <button name="rarBtn" onclick="showRar()" class="token">RAR</button> | |
| <button name="tokenBtn" onclick="showToken()" class="token">AccessToken</button> | |
| <button name="idTokenBtn" onclick="showIdToken()" class="idToken">IDToken</button> | |
| <button name="userinfoBtn" onclick="showUserInfo()" class="userinfo">Userinfo</button> | |
| <button name="profileBtn" onclick="showProfile()" class="profile">Profile</button> | |
| <button name="reauthBtn" onclick="enforceCurrentAuth()" class="reauth">ReAuth</button> | |
| <button name="changePasswordBtn" onclick="changePassword()" class="password">Password</button> | |
| <button name="accountBtn" onclick="keycloak.accountManagement()" class="account">Account</button> | |
| <button name="logoutBtn" onclick="keycloak.logout()" class="logout">Logout</button> | |
| </div> | |
| <div id="data" class="content"></div> | |
| </div> | |
| <script defer> | |
| let searchParams = new URLSearchParams(window.location.search); | |
| let keycloakBaseUrl = searchParams.get("base_url") || "http://localhost:8081"; | |
| let keycloakUrl = keycloakBaseUrl + (searchParams.get("path") || "/auth"); | |
| let realm = searchParams.get("realm") || 'demo'; | |
| let clientId = searchParams.get("client_id") || 'demo-client'; | |
| let scope = searchParams.get("scope") || 'openid email'; | |
| // &show=profile,settings,apps,security,logout | |
| const allContextClasses = ["profile", "token", "idToken", "userinfo", "logout", "apps", "password", "reauth", "account"]; | |
| const contextClassesToHideDefault = ["apps", "password", "reauth", "account", "profile"]; | |
| const contextClassesToShowDefault = [...allContextClasses].filter((value, index, arr) => { | |
| return !contextClassesToHideDefault.includes(value); | |
| }); | |
| let contextClassesToShow = searchParams.get("show")?.split(",") || contextClassesToShowDefault; | |
| for (let className of allContextClasses) { | |
| if (!contextClassesToShow.includes(className)) { | |
| let btn = document.querySelector(`button.${className}`); | |
| if (btn) { | |
| btn.parentElement.removeChild(btn); | |
| } | |
| } | |
| } | |
| document.getElementById("clientInfo").textContent = clientId; | |
| // dynamically add keycloak.js script | |
| let script = document.createElement('script'); | |
| script.type = 'text/javascript'; | |
| script.src = keycloakUrl + "/js/keycloak.js"; | |
| document.getElementsByTagName('head')[0].appendChild(script); | |
| window.onload = () => { | |
| window.keycloak = new Keycloak({ | |
| url: keycloakUrl, | |
| realm: realm, | |
| clientId: clientId | |
| }); | |
| let origLoginUrl = window.keycloak.createLoginUrl; | |
| window.keycloak.createLoginUrl = function (options) { | |
| let url = origLoginUrl.call(window.keycloak, options); | |
| // add RAR authorization_details if present | |
| if (options && options["authorizationDetails"]) { | |
| url += "&authorization_details=" + options["authorizationDetails"]; | |
| } | |
| return url; | |
| }; | |
| let initConfig = { | |
| onLoad: 'login-required', // redirects to login if not login | |
| // onLoad: 'check-sso', // shows login button of not logged in | |
| checkLoginIframe: true, | |
| checkLoginIframeInterval: 1, | |
| pkceMethod: 'S256', | |
| scope: scope | |
| }; | |
| let onLoginSuccess = () => { | |
| if (keycloak.authenticated) { | |
| showProfile(); | |
| } else { | |
| showWelcome(); | |
| } | |
| }; | |
| keycloak.init(initConfig).success(onLoginSuccess); | |
| keycloak.onAuthLogout = showWelcome; | |
| }; | |
| function showRar() { | |
| let data = [ | |
| { | |
| "type": "payment_initiation", | |
| "actions": [ | |
| "initiate", | |
| "status", | |
| "cancel" | |
| ], | |
| "locations": [ | |
| "https://example.com/payments" | |
| ], | |
| "instructedAmount": { | |
| "currency": "EUR", | |
| "amount": "123.50" | |
| }, | |
| "creditorName": "Merchant123", | |
| "creditorAccount": { | |
| "iban": "DE02100100109307118603" | |
| }, | |
| "remittanceInformationUnstructured": "Ref Number Merchant" | |
| } | |
| ]; | |
| let jsonString = JSON.stringify(data, null, ' ').trim(); | |
| let rarHtml = ` | |
| <button onclick="sendRar()">Authorize</button> | |
| <div id="rarData" contenteditable class="token-content">${jsonString}</div> | |
| `; | |
| show(rarHtml, "token-content"); | |
| } | |
| function sendRar() { | |
| let rarDataJson = document.getElementById("rarData").textContent; | |
| let encodedRarData = encodeURIComponent(rarDataJson); | |
| keycloak.login({ | |
| authorizationDetails: encodedRarData | |
| }); | |
| // kc.loginWithAuthorizationDetails(); | |
| } | |
| function showWelcome() { | |
| document.getElementById("welcome").classList.remove("hidden"); | |
| document.getElementById("content").classList.add("hidden"); | |
| } | |
| function getTimeSinceLastAuth() { | |
| let timeSinceAuthInSeconds = Math.floor((Date.now() - (keycloak.tokenParsed.auth_time * 1000)) / 1000); | |
| return timeSinceAuthInSeconds; | |
| } | |
| function enforceCurrentAuth() { | |
| let timeSinceAuthSeconds = getTimeSinceLastAuth(); | |
| console.log("time since auth: " + timeSinceAuthSeconds); | |
| if (timeSinceAuthSeconds < 10) { | |
| console.log("auth is still file") | |
| return; | |
| } else { | |
| console.log("trigger reauth") | |
| } | |
| keycloak.login({ | |
| loginHint: keycloak.tokenParsed.preferred_username, | |
| maxAge: 12 | |
| }); | |
| } | |
| function formatDate(timestamp) { | |
| return new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(timestamp)) | |
| } | |
| function changePassword() { | |
| keycloak.login({ | |
| action: "UPDATE_PASSWORD" | |
| }); | |
| } | |
| function showProfile() { | |
| let profileHtml = ` | |
| <table class="profile"> | |
| <tr> | |
| <td class="label">First name</td> | |
| <td><span id="firstName">${keycloak.tokenParsed['given_name']}</span></td> | |
| <td></td> | |
| </tr> | |
| <tr> | |
| <td class="label">Last name</td> | |
| <td><span id="lastName">${keycloak.tokenParsed['family_name']}</span></td> | |
| <td></td> | |
| </tr> | |
| <tr> | |
| <td class="label">Email</td> | |
| <td><span id="email">${keycloak.tokenParsed['email']}</span></td> | |
| <td></td> | |
| </tr> | |
| </table> | |
| `; | |
| show(profileHtml, "message-content"); | |
| } | |
| function showToken() { | |
| let data = JSON.stringify(keycloak.tokenParsed, null, ' '); | |
| show(data, "token-content"); | |
| } | |
| function showIdToken() { | |
| let data = JSON.stringify(keycloak.idTokenParsed, null, ' '); | |
| show(data, "token-content"); | |
| } | |
| async function showUserInfo() { | |
| await keycloak.updateToken(5); | |
| let userInfoData = await keycloak.loadUserInfo(); | |
| let data = JSON.stringify(userInfoData, null, ' '); | |
| show(data, "token-content"); | |
| } | |
| function show(data, cssClass) { | |
| let contentElement = document.getElementById('content'); | |
| contentElement.classList.remove("hidden") | |
| let dataElement = document.getElementById('data'); | |
| dataElement.innerHTML = data; | |
| dataElement.classList.remove(["message-content", "token-content"]); | |
| dataElement.classList.add(cssClass); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Linking my account TrustwalltGuide on Twitter with my address 0x40d2c12caacffc547e9cf2c60c02c9ec0b28eae5 on EVM in mycryptoprofile.io, and the challenge code is: 9cc97dfa0b56f9c0fa1a362c24a17b4b. #LitentryVerifyMyAddress