Compare commits
16 Commits
v8.2.2-rc.
...
v8.3.2
Author | SHA1 | Date | |
---|---|---|---|
c8b85c43aa | |||
e918788c3f | |||
b53f4f997c | |||
481d93ebc4 | |||
1ce666f136 | |||
49a8e702bc | |||
5d59e652d7 | |||
02af8c7311 | |||
fadf4e867c | |||
0839859fef | |||
c122b48e35 | |||
cebb297bbf | |||
2e31b796f7 | |||
e0a61b51cb | |||
46e50e622b | |||
7cfa1df0b2 |
@ -177,6 +177,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "celinepelletier",
|
||||
"name": "Céline Pelletier",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/82821620?v=4",
|
||||
"profile": "https://github.com/celinepelletier",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@ -118,6 +118,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://poelhekke.dev"><img src="https://avatars.githubusercontent.com/u/1632377?v=4?s=100" width="100px;" alt="Koen Poelhekke"/><br /><sub><b>Koen Poelhekke</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kpoelhekke" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zavoloklom"><img src="https://avatars.githubusercontent.com/u/4151869?v=4?s=100" width="100px;" alt="Sergey Kupletsky"/><br /><sub><b>Sergey Kupletsky</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rome-user"><img src="https://avatars.githubusercontent.com/u/114131048?v=4?s=100" width="100px;" alt="rome-user"/><br /><sub><b>rome-user</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=rome-user" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/celinepelletier"><img src="https://avatars.githubusercontent.com/u/82821620?v=4?s=100" width="100px;" alt="Céline Pelletier"/><br /><sub><b>Céline Pelletier</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=celinepelletier" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "8.2.2-rc.0",
|
||||
"version": "8.3.2",
|
||||
"description": "Create Keycloak themes using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -10,6 +10,8 @@ export const loginThemePageIds = [
|
||||
"login-reset-password.ftl",
|
||||
"login-verify-email.ftl",
|
||||
"terms.ftl",
|
||||
"login-oauth2-device-verify-user-code.ftl",
|
||||
"login-oauth-grant.ftl",
|
||||
"login-otp.ftl",
|
||||
"login-update-profile.ftl",
|
||||
"login-update-password.ftl",
|
||||
|
@ -12,6 +12,8 @@ const Error = lazy(() => import("keycloakify/login/pages/Error"));
|
||||
const LoginResetPassword = lazy(() => import("keycloakify/login/pages/LoginResetPassword"));
|
||||
const LoginVerifyEmail = lazy(() => import("keycloakify/login/pages/LoginVerifyEmail"));
|
||||
const Terms = lazy(() => import("keycloakify/login/pages/Terms"));
|
||||
const LoginDeviceVerifyUserCode = lazy(() => import("keycloakify/login/pages/LoginDeviceVerifyUserCode"));
|
||||
const LoginOauthGrant = lazy(() => import("keycloakify/login/pages/LoginOauthGrant"));
|
||||
const LoginOtp = lazy(() => import("keycloakify/login/pages/LoginOtp"));
|
||||
const LoginPassword = lazy(() => import("keycloakify/login/pages/LoginPassword"));
|
||||
const LoginUsername = lazy(() => import("keycloakify/login/pages/LoginUsername"));
|
||||
@ -52,6 +54,10 @@ export default function Fallback(props: PageProps<KcContext, I18n>) {
|
||||
return <LoginVerifyEmail kcContext={kcContext} {...rest} />;
|
||||
case "terms.ftl":
|
||||
return <Terms kcContext={kcContext} {...rest} />;
|
||||
case "login-oauth2-device-verify-user-code.ftl":
|
||||
return <LoginDeviceVerifyUserCode kcContext={kcContext} {...rest} />;
|
||||
case "login-oauth-grant.ftl":
|
||||
return <LoginOauthGrant kcContext={kcContext} {...rest} />;
|
||||
case "login-otp.ftl":
|
||||
return <LoginOtp kcContext={kcContext} {...rest} />;
|
||||
case "login-username.ftl":
|
||||
|
@ -94,4 +94,5 @@ export type ClassKey =
|
||||
| "kcSelectOTPListItemClass"
|
||||
| "kcAuthenticatorOtpCircleClass"
|
||||
| "kcSelectOTPItemHeadingClass"
|
||||
| "kcFormOptionsWrapperClass";
|
||||
| "kcFormOptionsWrapperClass"
|
||||
| "kcFormButtonsWrapperClass";
|
||||
|
@ -18,6 +18,8 @@ export type KcContext =
|
||||
| KcContext.LoginResetPassword
|
||||
| KcContext.LoginVerifyEmail
|
||||
| KcContext.Terms
|
||||
| KcContext.LoginDeviceVerifyUserCode
|
||||
| KcContext.LoginOauthGrant
|
||||
| KcContext.LoginOtp
|
||||
| KcContext.LoginUsername
|
||||
| KcContext.WebauthnAuthenticate
|
||||
@ -241,6 +243,27 @@ export declare namespace KcContext {
|
||||
pageId: "terms.ftl";
|
||||
};
|
||||
|
||||
export type LoginDeviceVerifyUserCode = Common & {
|
||||
pageId: "login-oauth2-device-verify-user-code.ftl";
|
||||
url: {
|
||||
oauth2DeviceVerificationAction: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginOauthGrant = Common & {
|
||||
pageId: "login-oauth-grant.ftl";
|
||||
oauth: {
|
||||
code: string;
|
||||
client: string;
|
||||
clientScopesRequested: {
|
||||
consentScreenText: string;
|
||||
}[];
|
||||
};
|
||||
url: {
|
||||
oauthAction: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginOtp = Common & {
|
||||
pageId: "login-otp.ftl";
|
||||
otpLogin: {
|
||||
|
@ -240,7 +240,9 @@ export const kcContextCommonMock: KcContext.Common = {
|
||||
const loginUrl = {
|
||||
...kcContextCommonMock.url,
|
||||
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
|
||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"oauth2DeviceVerificationAction": "/auth/realms/myrealm/device",
|
||||
"oauthAction": "/auth/realms/myrealm/login-actions/consent?client_id=account&tab_id=HoAx28ja4xg"
|
||||
};
|
||||
|
||||
export const kcContextMocks = [
|
||||
@ -344,6 +346,25 @@ export const kcContextMocks = [
|
||||
...kcContextCommonMock,
|
||||
"pageId": "terms.ftl"
|
||||
}),
|
||||
id<KcContext.LoginDeviceVerifyUserCode>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-oauth2-device-verify-user-code.ftl",
|
||||
url: loginUrl
|
||||
}),
|
||||
id<KcContext.LoginOauthGrant>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-oauth-grant.ftl",
|
||||
oauth: {
|
||||
code: "5-1N4CIzfi1aprIQjmylI-9e3spLCWW9i5d-GDcs-Sw",
|
||||
clientScopesRequested: [
|
||||
{ consentScreenText: "${profileScopeConsentText}" },
|
||||
{ consentScreenText: "${rolesScopeConsentText}" },
|
||||
{ consentScreenText: "${emailScopeConsentText}" }
|
||||
],
|
||||
client: "account"
|
||||
},
|
||||
url: loginUrl
|
||||
}),
|
||||
id<KcContext.LoginOtp>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-otp.ftl",
|
||||
|
@ -45,6 +45,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
|
||||
"kcInputClass": "form-control",
|
||||
"kcInputErrorMessageClass": "pf-c-form__helper-text pf-m-error required kc-feedback-text",
|
||||
"kcInputWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||
"kcFormButtonsWrapperClass": undefined,
|
||||
"kcFormOptionsClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||
"kcFormButtonsClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
||||
"kcFormSettingClass": "login-pf-settings",
|
||||
|
@ -32,7 +32,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
|
||||
<ul id="kc-totp-supported-apps">
|
||||
{totp.supportedApplications.map(app => (
|
||||
<li>{msgStr(app as MessageKey, app)}</li>
|
||||
<li>{msg(app as MessageKey)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
|
68
src/login/pages/LoginDeviceVerifyUserCode.tsx
Normal file
68
src/login/pages/LoginDeviceVerifyUserCode.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import Template from "../Template";
|
||||
import { I18n } from "../i18n";
|
||||
import { KcContext } from "../kcContext";
|
||||
import { useGetClassName } from "../lib/useGetClassName";
|
||||
import { PageProps } from "./PageProps";
|
||||
|
||||
export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth2-device-verify-user-code.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, classes } = props;
|
||||
const { url } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
return (
|
||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("oauth2DeviceVerificationTitle")}>
|
||||
<form
|
||||
id="kc-user-verify-device-user-code-form"
|
||||
className={getClassName("kcFormClass")}
|
||||
action={url.oauth2DeviceVerificationAction}
|
||||
method="post"
|
||||
>
|
||||
<div className={getClassName("kcFormGroupClass")}>
|
||||
<div className={getClassName("kcLabelWrapperClass")}>
|
||||
<label htmlFor="device-user-code" className={getClassName("kcLabelClass")}>
|
||||
{msg("verifyOAuth2DeviceUserCode")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={getClassName("kcInputWrapperClass")}>
|
||||
<input
|
||||
id="device-user-code"
|
||||
name="device_user_code"
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
className={getClassName("kcInputClass")}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={getClassName("kcFormGroupClass")}>
|
||||
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
|
||||
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
|
||||
<div className={getClassName("kcFormButtonsWrapperClass")}>
|
||||
<input
|
||||
className={clsx(
|
||||
getClassName("kcButtonClass"),
|
||||
getClassName("kcButtonPrimaryClass"),
|
||||
getClassName("kcButtonLargeClass")
|
||||
)}
|
||||
type="submit"
|
||||
value={msgStr("doSubmit")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Template>
|
||||
);
|
||||
}
|
73
src/login/pages/LoginOauthGrant.tsx
Normal file
73
src/login/pages/LoginOauthGrant.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { PageProps } from "./PageProps";
|
||||
import { KcContext } from "../kcContext";
|
||||
import { I18n } from "../i18n";
|
||||
import Template from "../Template";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
|
||||
export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth-grant.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, classes } = props;
|
||||
const { url, oauth, client } = kcContext;
|
||||
|
||||
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, doUseDefaultCss, classes }}
|
||||
headerNode={msg("oauthGrantTitle", client.name ? advancedMsgStr(client.name) : client.clientId)}
|
||||
>
|
||||
<div id="kc-oauth" className="content-area">
|
||||
<h3>{msg("oauthGrantRequest")}</h3>
|
||||
<ul>
|
||||
{oauth.clientScopesRequested.map(clientScope => (
|
||||
<li key={clientScope.consentScreenText}>
|
||||
<span>{advancedMsg(clientScope.consentScreenText)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<form className="form-actions" action={url.oauthAction} method="POST">
|
||||
<input type="hidden" name="code" value={oauth.code} />
|
||||
<div className={getClassName("kcFormGroupClass")}>
|
||||
<div id="kc-form-options">
|
||||
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons">
|
||||
<div className={getClassName("kcFormButtonsWrapperClass")}>
|
||||
<input
|
||||
className={clsx(
|
||||
getClassName("kcButtonClass"),
|
||||
getClassName("kcButtonPrimaryClass"),
|
||||
getClassName("kcButtonLargeClass")
|
||||
)}
|
||||
name="accept"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
value={msgStr("doYes")}
|
||||
/>
|
||||
<input
|
||||
className={clsx(
|
||||
getClassName("kcButtonClass"),
|
||||
getClassName("kcButtonDefaultClass"),
|
||||
getClassName("kcButtonLargeClass")
|
||||
)}
|
||||
name="cancel"
|
||||
id="kc-cancel"
|
||||
type="submit"
|
||||
value={msgStr("doNo")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="clearfix"></div>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
24
stories/login/pages/LoginDeviceVerifyUserCode.stories.tsx
Normal file
24
stories/login/pages/LoginDeviceVerifyUserCode.stories.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import type { ComponentMeta } from "@storybook/react";
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const pageId = "login-oauth2-device-verify-user-code.ftl";
|
||||
|
||||
const { PageStory } = createPageStory({ pageId });
|
||||
|
||||
const meta: ComponentMeta<any> = {
|
||||
title: `login/${pageId}`,
|
||||
component: PageStory,
|
||||
parameters: {
|
||||
viewMode: "story",
|
||||
previewTabs: {
|
||||
"storybook/docs/panel": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default = () => <PageStory />;
|
24
stories/login/pages/LoginOauthGrant.stories.tsx
Normal file
24
stories/login/pages/LoginOauthGrant.stories.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import type { ComponentMeta } from "@storybook/react";
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const pageId = "login-oauth-grant.ftl";
|
||||
|
||||
const { PageStory } = createPageStory({ pageId });
|
||||
|
||||
const meta: ComponentMeta<any> = {
|
||||
title: `login/${pageId}`,
|
||||
component: PageStory,
|
||||
parameters: {
|
||||
viewMode: "story",
|
||||
previewTabs: {
|
||||
"storybook/docs/panel": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default = () => <PageStory />;
|
Reference in New Issue
Block a user