keycloakify 9.5.8 → 9.6.0
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/account/Fallback.js +12 -0
- package/account/Fallback.js.map +1 -1
- package/account/TemplateProps.d.ts +1 -1
- package/account/kcContext/KcContext.d.ts +144 -1
- package/account/kcContext/KcContext.js.map +1 -1
- package/account/kcContext/kcContextMocks.js +45 -1
- package/account/kcContext/kcContextMocks.js.map +1 -1
- package/account/lib/useGetClassName.js +8 -1
- package/account/lib/useGetClassName.js.map +1 -1
- package/account/pages/Applications.d.ts +7 -0
- package/account/pages/Applications.js +24 -0
- package/account/pages/Applications.js.map +1 -0
- package/account/pages/Log.d.ts +7 -0
- package/account/pages/Log.js +13 -0
- package/account/pages/Log.js.map +1 -0
- package/account/pages/Sessions.d.ts +7 -0
- package/account/pages/Sessions.js +18 -0
- package/account/pages/Sessions.js.map +1 -0
- package/account/pages/Totp.d.ts +7 -0
- package/account/pages/Totp.js +20 -0
- package/account/pages/Totp.js.map +1 -0
- package/bin/keycloakify/generateFtl/pageId.d.ts +1 -1
- package/bin/keycloakify/generateFtl/pageId.js +1 -1
- package/bin/keycloakify/generateFtl/pageId.js.map +1 -1
- package/package.json +21 -1
- package/src/account/Fallback.tsx +12 -0
- package/src/account/TemplateProps.ts +14 -1
- package/src/account/kcContext/KcContext.ts +148 -1
- package/src/account/kcContext/kcContextMocks.ts +68 -0
- package/src/account/lib/useGetClassName.ts +8 -1
- package/src/account/pages/Applications.tsx +138 -0
- package/src/account/pages/Log.tsx +70 -0
- package/src/account/pages/Sessions.tsx +68 -0
- package/src/account/pages/Totp.tsx +236 -0
- package/src/bin/keycloakify/generateFtl/pageId.ts +1 -1
@@ -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];
|