keycloakify 6.4.3 → 6.5.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/bin/keycloakify/generateFtl/generateFtl.d.ts +1 -1
- package/bin/keycloakify/generateFtl/generateFtl.js +1 -0
- package/bin/keycloakify/generateFtl/generateFtl.js.map +1 -1
- package/bin/tsconfig.tsbuildinfo +1 -1
- package/lib/components/KcApp.js +3 -0
- package/lib/components/KcApp.js.map +1 -1
- package/lib/components/LoginUsername.d.ts +10 -0
- package/lib/components/LoginUsername.js +73 -0
- package/lib/components/LoginUsername.js.map +1 -0
- package/lib/getKcContext/KcContextBase.d.ts +30 -1
- package/lib/getKcContext/KcContextBase.js.map +1 -1
- package/lib/getKcContext/kcContextMocks/kcContextMocks.js +5 -0
- package/lib/getKcContext/kcContextMocks/kcContextMocks.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -1
- package/src/bin/keycloakify/generateFtl/generateFtl.ts +1 -0
- package/src/lib/components/KcApp.tsx +3 -0
- package/src/lib/components/LoginUsername.tsx +168 -0
- package/src/lib/getKcContext/KcContextBase.ts +31 -0
- package/src/lib/getKcContext/kcContextMocks/kcContextMocks.ts +21 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "keycloakify",
|
3
|
-
"version": "6.
|
3
|
+
"version": "6.5.0",
|
4
4
|
"description": "Keycloak theme generator for Reacts app",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -73,6 +73,7 @@
|
|
73
73
|
"src/lib/components/LoginResetPassword.tsx",
|
74
74
|
"src/lib/components/LoginUpdatePassword.tsx",
|
75
75
|
"src/lib/components/LoginUpdateProfile.tsx",
|
76
|
+
"src/lib/components/LoginUsername.tsx",
|
76
77
|
"src/lib/components/LoginVerifyEmail.tsx",
|
77
78
|
"src/lib/components/LogoutConfirm.tsx",
|
78
79
|
"src/lib/components/Register.tsx",
|
@@ -476,6 +477,9 @@
|
|
476
477
|
"lib/components/LoginUpdateProfile.d.ts",
|
477
478
|
"lib/components/LoginUpdateProfile.js",
|
478
479
|
"lib/components/LoginUpdateProfile.js.map",
|
480
|
+
"lib/components/LoginUsername.d.ts",
|
481
|
+
"lib/components/LoginUsername.js",
|
482
|
+
"lib/components/LoginUsername.js.map",
|
479
483
|
"lib/components/LoginVerifyEmail.d.ts",
|
480
484
|
"lib/components/LoginVerifyEmail.js",
|
481
485
|
"lib/components/LoginVerifyEmail.js.map",
|
@@ -1259,6 +1263,7 @@
|
|
1259
1263
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
1260
1264
|
},
|
1261
1265
|
"devDependencies": {
|
1266
|
+
"@babel/core": "^7.0.0",
|
1262
1267
|
"@emotion/react": "^11.4.1",
|
1263
1268
|
"@types/memoizee": "^0.4.7",
|
1264
1269
|
"@types/minimist": "^1.2.2",
|
@@ -13,6 +13,7 @@ import { Reflect } from "tsafe/Reflect";
|
|
13
13
|
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
|
14
14
|
export const pageIds = [
|
15
15
|
"login.ftl",
|
16
|
+
"login-username.ftl",
|
16
17
|
"register.ftl",
|
17
18
|
"register-user-profile.ftl",
|
18
19
|
"info.ftl",
|
@@ -13,6 +13,7 @@ const LoginResetPassword = lazy(() => import("./LoginResetPassword"));
|
|
13
13
|
const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail"));
|
14
14
|
const Terms = lazy(() => import("./Terms"));
|
15
15
|
const LoginOtp = lazy(() => import("./LoginOtp"));
|
16
|
+
const LoginUsername = lazy(() => import("./LoginUsername"));
|
16
17
|
const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword"));
|
17
18
|
const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile"));
|
18
19
|
const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm"));
|
@@ -67,6 +68,8 @@ const KcApp = memo(
|
|
67
68
|
return <Terms {...{ kcContext, ...props }} />;
|
68
69
|
case "login-otp.ftl":
|
69
70
|
return <LoginOtp {...{ kcContext, ...props }} />;
|
71
|
+
case "login-username.ftl":
|
72
|
+
return <LoginUsername {...{ kcContext, ...props }} />;
|
70
73
|
case "login-update-password.ftl":
|
71
74
|
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
|
72
75
|
case "login-update-profile.ftl":
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import React, { useState, memo } from "react";
|
2
|
+
import Template from "./Template";
|
3
|
+
import type { KcProps } from "./KcProps";
|
4
|
+
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
5
|
+
import { useCssAndCx } from "../tools/useCssAndCx";
|
6
|
+
import { useConstCallback } from "powerhooks/useConstCallback";
|
7
|
+
import type { FormEventHandler } from "react";
|
8
|
+
import type { I18n } from "../i18n";
|
9
|
+
|
10
|
+
const LoginUsername = memo(
|
11
|
+
({
|
12
|
+
kcContext,
|
13
|
+
i18n,
|
14
|
+
doFetchDefaultThemeResources = true,
|
15
|
+
...props
|
16
|
+
}: { kcContext: KcContextBase.LoginUsername; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
17
|
+
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
|
18
|
+
|
19
|
+
const { msg, msgStr } = i18n;
|
20
|
+
|
21
|
+
const { cx } = useCssAndCx();
|
22
|
+
|
23
|
+
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
24
|
+
|
25
|
+
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
26
|
+
e.preventDefault();
|
27
|
+
|
28
|
+
setIsLoginButtonDisabled(true);
|
29
|
+
|
30
|
+
const formElement = e.target as HTMLFormElement;
|
31
|
+
|
32
|
+
//NOTE: Even if we login with email Keycloak expect username and password in
|
33
|
+
//the POST request.
|
34
|
+
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
35
|
+
|
36
|
+
formElement.submit();
|
37
|
+
});
|
38
|
+
|
39
|
+
return (
|
40
|
+
<Template
|
41
|
+
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
42
|
+
displayInfo={social.displayInfo}
|
43
|
+
displayWide={realm.password && social.providers !== undefined}
|
44
|
+
headerNode={msg("doLogIn")}
|
45
|
+
formNode={
|
46
|
+
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && props.kcContentWrapperClass)}>
|
47
|
+
<div
|
48
|
+
id="kc-form-wrapper"
|
49
|
+
className={cx(
|
50
|
+
realm.password && social.providers && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass]
|
51
|
+
)}
|
52
|
+
>
|
53
|
+
{realm.password && (
|
54
|
+
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
55
|
+
<div className={cx(props.kcFormGroupClass)}>
|
56
|
+
{!usernameHidden &&
|
57
|
+
(() => {
|
58
|
+
const label = !realm.loginWithEmailAllowed
|
59
|
+
? "username"
|
60
|
+
: realm.registrationEmailAsUsername
|
61
|
+
? "email"
|
62
|
+
: "usernameOrEmail";
|
63
|
+
|
64
|
+
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
65
|
+
|
66
|
+
return (
|
67
|
+
<>
|
68
|
+
<label htmlFor={autoCompleteHelper} className={cx(props.kcLabelClass)}>
|
69
|
+
{msg(label)}
|
70
|
+
</label>
|
71
|
+
<input
|
72
|
+
tabIndex={1}
|
73
|
+
id={autoCompleteHelper}
|
74
|
+
className={cx(props.kcInputClass)}
|
75
|
+
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
76
|
+
//the browser how to pre fill the form but before submit we put it back
|
77
|
+
//to username because it is what keycloak expects.
|
78
|
+
name={autoCompleteHelper}
|
79
|
+
defaultValue={login.username ?? ""}
|
80
|
+
type="text"
|
81
|
+
autoFocus={true}
|
82
|
+
autoComplete="off"
|
83
|
+
/>
|
84
|
+
</>
|
85
|
+
);
|
86
|
+
})()}
|
87
|
+
</div>
|
88
|
+
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
89
|
+
<div id="kc-form-options">
|
90
|
+
{realm.rememberMe && !usernameHidden && (
|
91
|
+
<div className="checkbox">
|
92
|
+
<label>
|
93
|
+
<input
|
94
|
+
tabIndex={3}
|
95
|
+
id="rememberMe"
|
96
|
+
name="rememberMe"
|
97
|
+
type="checkbox"
|
98
|
+
{...(login.rememberMe
|
99
|
+
? {
|
100
|
+
"checked": true
|
101
|
+
}
|
102
|
+
: {})}
|
103
|
+
/>
|
104
|
+
{msg("rememberMe")}
|
105
|
+
</label>
|
106
|
+
</div>
|
107
|
+
)}
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
111
|
+
<input
|
112
|
+
tabIndex={4}
|
113
|
+
className={cx(
|
114
|
+
props.kcButtonClass,
|
115
|
+
props.kcButtonPrimaryClass,
|
116
|
+
props.kcButtonBlockClass,
|
117
|
+
props.kcButtonLargeClass
|
118
|
+
)}
|
119
|
+
name="login"
|
120
|
+
id="kc-login"
|
121
|
+
type="submit"
|
122
|
+
value={msgStr("doLogIn")}
|
123
|
+
disabled={isLoginButtonDisabled}
|
124
|
+
/>
|
125
|
+
</div>
|
126
|
+
</form>
|
127
|
+
)}
|
128
|
+
</div>
|
129
|
+
{realm.password && social.providers !== undefined && (
|
130
|
+
<div id="kc-social-providers" className={cx(props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass)}>
|
131
|
+
<ul
|
132
|
+
className={cx(
|
133
|
+
props.kcFormSocialAccountListClass,
|
134
|
+
social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass
|
135
|
+
)}
|
136
|
+
>
|
137
|
+
{social.providers.map(p => (
|
138
|
+
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
139
|
+
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
140
|
+
<span>{p.displayName}</span>
|
141
|
+
</a>
|
142
|
+
</li>
|
143
|
+
))}
|
144
|
+
</ul>
|
145
|
+
</div>
|
146
|
+
)}
|
147
|
+
</div>
|
148
|
+
}
|
149
|
+
infoNode={
|
150
|
+
realm.password &&
|
151
|
+
realm.registrationAllowed &&
|
152
|
+
!registrationDisabled && (
|
153
|
+
<div id="kc-registration">
|
154
|
+
<span>
|
155
|
+
{msg("noAccount")}
|
156
|
+
<a tabIndex={6} href={url.registrationUrl}>
|
157
|
+
{msg("doRegister")}
|
158
|
+
</a>
|
159
|
+
</span>
|
160
|
+
</div>
|
161
|
+
)
|
162
|
+
}
|
163
|
+
/>
|
164
|
+
);
|
165
|
+
}
|
166
|
+
);
|
167
|
+
|
168
|
+
export default LoginUsername;
|
@@ -19,6 +19,7 @@ export type KcContextBase =
|
|
19
19
|
| KcContextBase.LoginVerifyEmail
|
20
20
|
| KcContextBase.Terms
|
21
21
|
| KcContextBase.LoginOtp
|
22
|
+
| KcContextBase.LoginUsername
|
22
23
|
| KcContextBase.LoginUpdatePassword
|
23
24
|
| KcContextBase.LoginUpdateProfile
|
24
25
|
| KcContextBase.LoginIdpLinkConfirm
|
@@ -198,6 +199,36 @@ export declare namespace KcContextBase {
|
|
198
199
|
};
|
199
200
|
};
|
200
201
|
|
202
|
+
export type LoginUsername = Common & {
|
203
|
+
pageId: "login-username.ftl";
|
204
|
+
url: {
|
205
|
+
loginResetCredentialsUrl: string;
|
206
|
+
registrationUrl: string;
|
207
|
+
};
|
208
|
+
realm: {
|
209
|
+
loginWithEmailAllowed: boolean;
|
210
|
+
rememberMe: boolean;
|
211
|
+
password: boolean;
|
212
|
+
resetPasswordAllowed: boolean;
|
213
|
+
registrationAllowed: boolean;
|
214
|
+
};
|
215
|
+
registrationDisabled: boolean;
|
216
|
+
login: {
|
217
|
+
username?: string;
|
218
|
+
rememberMe?: boolean;
|
219
|
+
};
|
220
|
+
usernameHidden?: boolean;
|
221
|
+
social: {
|
222
|
+
displayInfo: boolean;
|
223
|
+
providers?: {
|
224
|
+
loginUrl: string;
|
225
|
+
alias: string;
|
226
|
+
providerId: string;
|
227
|
+
displayName: string;
|
228
|
+
}[];
|
229
|
+
};
|
230
|
+
};
|
231
|
+
|
201
232
|
export type LoginUpdatePassword = Common & {
|
202
233
|
pageId: "login-update-password.ftl";
|
203
234
|
username: string;
|
@@ -359,6 +359,27 @@ export const kcContextMocks: KcContextBase[] = [
|
|
359
359
|
]
|
360
360
|
}
|
361
361
|
}),
|
362
|
+
id<KcContextBase.LoginUsername>({
|
363
|
+
...kcContextCommonMock,
|
364
|
+
"pageId": "login-username.ftl",
|
365
|
+
"url": loginUrl,
|
366
|
+
"realm": {
|
367
|
+
...kcContextCommonMock.realm,
|
368
|
+
"loginWithEmailAllowed": true,
|
369
|
+
"rememberMe": true,
|
370
|
+
"password": true,
|
371
|
+
"resetPasswordAllowed": true,
|
372
|
+
"registrationAllowed": true
|
373
|
+
},
|
374
|
+
"social": {
|
375
|
+
"displayInfo": true
|
376
|
+
},
|
377
|
+
"usernameHidden": false,
|
378
|
+
"login": {
|
379
|
+
"rememberMe": false
|
380
|
+
},
|
381
|
+
"registrationDisabled": false
|
382
|
+
}),
|
362
383
|
id<KcContextBase.LoginUpdatePassword>({
|
363
384
|
...kcContextCommonMock,
|
364
385
|
"pageId": "login-update-password.ftl",
|