keycloakify 9.5.8 → 9.6.1

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.
Files changed (39) hide show
  1. package/README.md +2 -2
  2. package/account/Fallback.js +12 -0
  3. package/account/Fallback.js.map +1 -1
  4. package/account/TemplateProps.d.ts +1 -1
  5. package/account/kcContext/KcContext.d.ts +144 -1
  6. package/account/kcContext/KcContext.js.map +1 -1
  7. package/account/kcContext/kcContextMocks.js +45 -1
  8. package/account/kcContext/kcContextMocks.js.map +1 -1
  9. package/account/lib/useGetClassName.js +8 -1
  10. package/account/lib/useGetClassName.js.map +1 -1
  11. package/account/pages/Applications.d.ts +7 -0
  12. package/account/pages/Applications.js +24 -0
  13. package/account/pages/Applications.js.map +1 -0
  14. package/account/pages/Log.d.ts +7 -0
  15. package/account/pages/Log.js +13 -0
  16. package/account/pages/Log.js.map +1 -0
  17. package/account/pages/Sessions.d.ts +7 -0
  18. package/account/pages/Sessions.js +18 -0
  19. package/account/pages/Sessions.js.map +1 -0
  20. package/account/pages/Totp.d.ts +7 -0
  21. package/account/pages/Totp.js +20 -0
  22. package/account/pages/Totp.js.map +1 -0
  23. package/bin/keycloakify/generateFtl/pageId.d.ts +1 -1
  24. package/bin/keycloakify/generateFtl/pageId.js +1 -1
  25. package/bin/keycloakify/generateFtl/pageId.js.map +1 -1
  26. package/bin/keycloakify/keycloakify.js +9 -4
  27. package/bin/keycloakify/keycloakify.js.map +1 -1
  28. package/package.json +21 -1
  29. package/src/account/Fallback.tsx +12 -0
  30. package/src/account/TemplateProps.ts +14 -1
  31. package/src/account/kcContext/KcContext.ts +148 -1
  32. package/src/account/kcContext/kcContextMocks.ts +68 -0
  33. package/src/account/lib/useGetClassName.ts +8 -1
  34. package/src/account/pages/Applications.tsx +138 -0
  35. package/src/account/pages/Log.tsx +70 -0
  36. package/src/account/pages/Sessions.tsx +68 -0
  37. package/src/account/pages/Totp.tsx +236 -0
  38. package/src/bin/keycloakify/generateFtl/pageId.ts +1 -1
  39. package/src/bin/keycloakify/keycloakify.ts +12 -6
@@ -0,0 +1,236 @@
1
+ import { clsx } from "keycloakify/tools/clsx";
2
+ import type { PageProps } from "keycloakify/account/pages/PageProps";
3
+ import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
4
+ import type { KcContext } from "../kcContext";
5
+ import type { I18n } from "../i18n";
6
+ import { MessageKey } from "keycloakify/account/i18n/i18n";
7
+
8
+ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp.ftl" }>, I18n>) {
9
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
10
+ const { getClassName } = useGetClassName({
11
+ doUseDefaultCss,
12
+ classes
13
+ });
14
+
15
+ const { totp, mode, url, messagesPerField, stateChecker } = kcContext;
16
+
17
+ const { msg, msgStr } = i18n;
18
+
19
+ const algToKeyUriAlg: Record<(typeof kcContext)["totp"]["policy"]["algorithm"], string> = {
20
+ "HmacSHA1": "SHA1",
21
+ "HmacSHA256": "SHA256",
22
+ "HmacSHA512": "SHA512"
23
+ };
24
+
25
+ return (
26
+ <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="totp">
27
+ <>
28
+ <div className="row">
29
+ <div className="col-md-10">
30
+ <h2>{msg("authenticatorTitle")}</h2>
31
+ </div>
32
+ {totp.otpCredentials.length === 0 && (
33
+ <div className="subtitle col-md-2">
34
+ <span className="required">*</span>
35
+ {msg("requiredFields")}
36
+ </div>
37
+ )}
38
+ </div>
39
+ {totp.enabled && (
40
+ <table className="table table-bordered table-striped">
41
+ <thead>
42
+ {totp.otpCredentials.length > 1 ? (
43
+ <tr>
44
+ <th colSpan={4}>{msg("configureAuthenticators")}</th>
45
+ </tr>
46
+ ) : (
47
+ <tr>
48
+ <th colSpan={3}>{msg("configureAuthenticators")}</th>
49
+ </tr>
50
+ )}
51
+ </thead>
52
+ <tbody>
53
+ {totp.otpCredentials.map((credential, index) => (
54
+ <tr key={index}>
55
+ <td className="provider">{msg("mobile")}</td>
56
+ {totp.otpCredentials.length > 1 && <td className="provider">{credential.id}</td>}
57
+ <td className="provider">{credential.userLabel || ""}</td>
58
+ <td className="action">
59
+ <form action={url.totpUrl} method="post" className="form-inline">
60
+ <input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
61
+ <input type="hidden" id="submitAction" name="submitAction" value="Delete" />
62
+ <input type="hidden" id="credentialId" name="credentialId" value={credential.id} />
63
+ <button id={`remove-mobile-${index}`} className="btn btn-default">
64
+ <i className="pficon pficon-delete"></i>
65
+ </button>
66
+ </form>
67
+ </td>
68
+ </tr>
69
+ ))}
70
+ </tbody>
71
+ </table>
72
+ )}
73
+ {!totp.enabled && (
74
+ <div>
75
+ <hr />
76
+ <ol id="kc-totp-settings">
77
+ <li>
78
+ <p>{msg("totpStep1")}</p>
79
+
80
+ <ul id="kc-totp-supported-apps">
81
+ {totp.supportedApplications.map(app => (
82
+ <li key={app}>{msg(app as MessageKey)}</li>
83
+ ))}
84
+ </ul>
85
+ </li>
86
+
87
+ {mode && mode == "manual" ? (
88
+ <>
89
+ <li>
90
+ <p>{msg("totpManualStep2")}</p>
91
+ <p>
92
+ <span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
93
+ </p>
94
+ <p>
95
+ <a href={totp.qrUrl} id="mode-barcode">
96
+ {msg("totpScanBarcode")}
97
+ </a>
98
+ </p>
99
+ </li>
100
+ <li>
101
+ <p>{msg("totpManualStep3")}</p>
102
+ <p>
103
+ <ul>
104
+ <li id="kc-totp-type">
105
+ {msg("totpType")}: {msg(`totp.${totp.policy.type}`)}
106
+ </li>
107
+ <li id="kc-totp-algorithm">
108
+ {msg("totpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
109
+ </li>
110
+ <li id="kc-totp-digits">
111
+ {msg("totpDigits")}: {totp.policy.digits}
112
+ </li>
113
+ {totp.policy.type === "totp" ? (
114
+ <li id="kc-totp-period">
115
+ {msg("totpInterval")}: {totp.policy.period}
116
+ </li>
117
+ ) : (
118
+ <li id="kc-totp-counter">
119
+ {msg("totpCounter")}: {totp.policy.initialCounter}
120
+ </li>
121
+ )}
122
+ </ul>
123
+ </p>
124
+ </li>
125
+ </>
126
+ ) : (
127
+ <li>
128
+ <p>{msg("totpStep2")}</p>
129
+ <p>
130
+ <img
131
+ id="kc-totp-secret-qr-code"
132
+ src={`data:image/png;base64, ${totp.totpSecretQrCode}`}
133
+ alt="Figure: Barcode"
134
+ />
135
+ </p>
136
+ <p>
137
+ <a href={totp.manualUrl} id="mode-manual">
138
+ {msg("totpUnableToScan")}
139
+ </a>
140
+ </p>
141
+ </li>
142
+ )}
143
+ <li>
144
+ <p>{msg("totpStep3")}</p>
145
+ <p>{msg("totpStep3DeviceName")}</p>
146
+ </li>
147
+ </ol>
148
+ <hr />
149
+ <form action={url.totpUrl} className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post">
150
+ <input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
151
+ <div className={getClassName("kcFormGroupClass")}>
152
+ <div className="col-sm-2 col-md-2">
153
+ <label htmlFor="totp" className="control-label">
154
+ {msg("authenticatorCode")}
155
+ </label>
156
+ <span className="required">*</span>
157
+ </div>
158
+ <div className="col-sm-10 col-md-10">
159
+ <input
160
+ type="text"
161
+ id="totp"
162
+ name="totp"
163
+ autoComplete="off"
164
+ className={getClassName("kcInputClass")}
165
+ aria-invalid={messagesPerField.existsError("totp")}
166
+ />
167
+
168
+ {messagesPerField.existsError("totp") && (
169
+ <span id="input-error-otp-code" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
170
+ {messagesPerField.get("totp")}
171
+ </span>
172
+ )}
173
+ </div>
174
+ <input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
175
+ {mode && <input type="hidden" id="mode" value={mode} />}
176
+ </div>
177
+
178
+ <div className={getClassName("kcFormGroupClass")}>
179
+ <div className="col-sm-2 col-md-2">
180
+ <label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
181
+ {msg("totpDeviceName")}
182
+ </label>
183
+ {totp.otpCredentials.length >= 1 && <span className="required">*</span>}
184
+ </div>
185
+ <div className="col-sm-10 col-md-10">
186
+ <input
187
+ type="text"
188
+ id="userLabel"
189
+ name="userLabel"
190
+ autoComplete="off"
191
+ className={getClassName("kcInputClass")}
192
+ aria-invalid={messagesPerField.existsError("userLabel")}
193
+ />
194
+ {messagesPerField.existsError("userLabel") && (
195
+ <span id="input-error-otp-label" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
196
+ {messagesPerField.get("userLabel")}
197
+ </span>
198
+ )}
199
+ </div>
200
+ </div>
201
+
202
+ <div id="kc-form-buttons" className={clsx(getClassName("kcFormGroupClass"), "text-right")}>
203
+ <div className={getClassName("kcInputWrapperClass")}>
204
+ <input
205
+ type="submit"
206
+ className={clsx(
207
+ getClassName("kcButtonClass"),
208
+ getClassName("kcButtonPrimaryClass"),
209
+ getClassName("kcButtonLargeClass")
210
+ )}
211
+ id="saveTOTPBtn"
212
+ value={msgStr("doSave")}
213
+ />
214
+ <button
215
+ type="submit"
216
+ className={clsx(
217
+ getClassName("kcButtonClass"),
218
+ getClassName("kcButtonDefaultClass"),
219
+ getClassName("kcButtonLargeClass"),
220
+ getClassName("kcButtonLargeClass")
221
+ )}
222
+ id="cancelTOTPBtn"
223
+ name="submitAction"
224
+ value="Cancel"
225
+ >
226
+ {msg("doCancel")}
227
+ </button>
228
+ </div>
229
+ </div>
230
+ </form>
231
+ </div>
232
+ )}
233
+ </>
234
+ </Template>
235
+ );
236
+ }
@@ -27,7 +27,7 @@ export const loginThemePageIds = [
27
27
  "saml-post-form.ftl"
28
28
  ] as const;
29
29
 
30
- export const accountThemePageIds = ["password.ftl", "account.ftl"] as const;
30
+ export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl", "applications.ftl", "log.ftl"] as const;
31
31
 
32
32
  export type LoginThemePageId = (typeof loginThemePageIds)[number];
33
33
  export type AccountThemePageId = (typeof accountThemePageIds)[number];
@@ -49,13 +49,19 @@ export async function main() {
49
49
 
50
50
  fs.writeFileSync(pathJoin(buildOptions.keycloakifyBuildDirPath, ".gitignore"), Buffer.from("*", "utf8"));
51
51
 
52
- child_process.execSync("npx vite", {
53
- "cwd": buildOptions.reactAppRootDirPath,
54
- "env": {
55
- ...process.env,
56
- [keycloakifyBuildOptionsForPostPostBuildScriptEnvName]: JSON.stringify(buildOptions)
52
+ run_post_build_script: {
53
+ if (buildOptions.bundler !== "vite") {
54
+ break run_post_build_script;
57
55
  }
58
- });
56
+
57
+ child_process.execSync("npx vite", {
58
+ "cwd": buildOptions.reactAppRootDirPath,
59
+ "env": {
60
+ ...process.env,
61
+ [keycloakifyBuildOptionsForPostPostBuildScriptEnvName]: JSON.stringify(buildOptions)
62
+ }
63
+ });
64
+ }
59
65
 
60
66
  create_jar: {
61
67
  if (!buildOptions.doCreateJar) {