Compare commits

...

19 Commits

Author SHA1 Message Date
Joseph Garrone
d24a8e99cc
Fix badge 2025-04-09 16:33:19 +02:00
Joseph Garrone
59d2d56091
Fix badge 2025-04-09 16:30:27 +02:00
Joseph Garrone
8515b7060a
Merge pull request #833 from keycloakify/all-contributors/add-wnmzzzz
docs: add wnmzzzz as a contributor for test
2025-04-09 16:29:50 +02:00
allcontributors[bot]
a076b3f4d0
docs: update .all-contributorsrc [skip ci] 2025-04-09 14:29:32 +00:00
allcontributors[bot]
1e3240ef35
docs: update README.md [skip ci] 2025-04-09 14:29:31 +00:00
Joseph Garrone
d767080dfe
Merge pull request #831 from wnmzzzz/patch-1
Add Story for AppInitiatedAction to UpdatePassword
2025-04-09 16:29:02 +02:00
wnmzzzz
b228eda488
Add Story for AppInitiatedAction to UpdatePassword 2025-04-07 08:42:46 +02:00
garronej
35f54964ce Bump version 2025-04-04 23:57:42 +02:00
garronej
759b834ccc Follow up on #827 2025-04-04 23:57:13 +02:00
Joseph Garrone
137e12cbbb
Merge pull request #830 from keycloakify/all-contributors/add-kodebach
docs: add kodebach as a contributor for code
2025-04-04 23:57:08 +02:00
allcontributors[bot]
57b08d9dea
docs: update .all-contributorsrc [skip ci] 2025-04-04 21:56:54 +00:00
allcontributors[bot]
1dd7b673a1
docs: update README.md [skip ci] 2025-04-04 21:56:53 +00:00
Joseph Garrone
b00ffc50c3
Merge pull request #827 from kodebach/fix-keycloak-36012
Fix double submit bug in OTP Form
2025-04-04 23:34:01 +02:00
Klemens Böswirth
f9db40d33d fix: https://github.com/keycloak/keycloak/issues/36012
adapted from https://github.com/keycloak/keycloak/pull/36096
2025-04-02 12:08:37 +00:00
Joseph Garrone
4bc6a843d8
Merge pull request #820 from kingjan1999/fix-required-actions-whitespace
fix: add whitespace before required actions in info page
2025-03-21 13:21:02 +01:00
Jan Beckmann
da3e7514f0
fix: add whitespace before required actions in info page 2025-03-21 11:37:08 +01:00
garronej
bc396bc41b Update runPrettier so that it will still work if we ever switch to ESM 2025-03-16 02:17:11 +01:00
garronej
947efe8d63 Bump version 2025-03-16 00:49:08 +01:00
garronej
64189bf8fe #815 2025-03-16 00:48:50 +01:00
10 changed files with 80 additions and 11 deletions

View File

@ -345,6 +345,24 @@
"contributions": [ "contributions": [
"doc" "doc"
] ]
},
{
"login": "kodebach",
"name": "Klemens Böswirth",
"avatar_url": "https://avatars.githubusercontent.com/u/23529132?v=4",
"profile": "https://github.com/kodebach",
"contributions": [
"code"
]
},
{
"login": "wnmzzzz",
"name": "wnmzzzz",
"avatar_url": "https://avatars.githubusercontent.com/u/117174301?v=4",
"profile": "https://github.com/wnmzzzz",
"contributions": [
"test"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@ -6,7 +6,7 @@
<br> <br>
<br> <br>
<a href="https://github.com/garronej/keycloakify/actions"> <a href="https://github.com/garronej/keycloakify/actions">
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main"> <img src="https://github.com/keycloakify/keycloakify/actions/workflows/ci.yaml/badge.svg">
</a> </a>
<a href="https://www.npmjs.com/package/keycloakify"> <a href="https://www.npmjs.com/package/keycloakify">
<img src="https://img.shields.io/npm/dm/keycloakify"> <img src="https://img.shields.io/npm/dm/keycloakify">
@ -171,6 +171,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<tr> <tr>
<td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td> <td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kodebach"><img src="https://avatars.githubusercontent.com/u/23529132?v=4?s=100" width="100px;" alt="Klemens Böswirth"/><br /><sub><b>Klemens Böswirth</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kodebach" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wnmzzzz"><img src="https://avatars.githubusercontent.com/u/117174301?v=4?s=100" width="100px;" alt="wnmzzzz"/><br /><sub><b>wnmzzzz</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=wnmzzzz" title="Tests">⚠️</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "11.8.21", "version": "11.8.23",
"description": "Framework to create custom Keycloak UIs", "description": "Framework to create custom Keycloak UIs",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -45,12 +45,12 @@ export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
`This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`, `This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
`To relinquish ownership and restore this file to its original content, run the following command:`, `To relinquish ownership and restore this file to its original content, run the following command:`,
``, ``,
`$ npx keycloakify own --path '${path}' --revert` `$ npx keycloakify own --path "${path}" --revert`
] ]
: [ : [
`WARNING: Before modifying this file, run the following command:`, `WARNING: Before modifying this file, run the following command:`,
``, ``,
`$ npx keycloakify own --path '${path}'`, `$ npx keycloakify own --path "${path}"`,
``, ``,
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`, `This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
`It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.` `It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`

View File

@ -14,6 +14,8 @@ export function getAbsoluteAndInOsFormatPath(params: {
let pathOut = pathIsh; let pathOut = pathIsh;
pathOut = pathOut.replace(/^['"]/, "").replace(/['"]$/, "");
pathOut = pathOut.replace(/\//g, pathSep); pathOut = pathOut.replace(/\//g, pathSep);
if (pathOut.startsWith("~")) { if (pathOut.startsWith("~")) {

View File

@ -52,10 +52,26 @@ export async function getPrettier(): Promise<PrettierAndConfigHash> {
// So we do a sketchy eval to bypass ncc. // So we do a sketchy eval to bypass ncc.
// We make sure to only do that when linking, otherwise we import properly. // We make sure to only do that when linking, otherwise we import properly.
if (readThisNpmPackageVersion().startsWith("0.0.0")) { if (readThisNpmPackageVersion().startsWith("0.0.0")) {
eval( const prettierDirPath = pathResolve(
`${symToStr({ prettier })} = require("${pathResolve(pathJoin(getNodeModulesBinDirPath({ packageJsonFilePath: undefined }), "..", "prettier"))}")` pathJoin(
getNodeModulesBinDirPath({ packageJsonFilePath: undefined }),
"..",
"prettier"
)
); );
const isCJS = typeof module !== "undefined" && module.exports;
if (isCJS) {
eval(`${symToStr({ prettier })} = require("${prettierDirPath}")`);
} else {
prettier = await new Promise(_resolve => {
eval(
`import("file:///${pathJoin(prettierDirPath, "index.mjs").replace(/\\/g, "/")}").then(prettier => _resolve(prettier))`
);
});
}
assert(!is<undefined>(prettier)); assert(!is<undefined>(prettier));
break import_prettier; break import_prettier;

View File

@ -31,10 +31,10 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: kcSanitize( __html: kcSanitize(
(() => { (() => {
let html = message.summary; let html = message.summary?.trim();
if (requiredActions) { if (requiredActions) {
html += "<b>"; html += " <b>";
html += requiredActions.map(requiredAction => advancedMsgStr(`requiredAction.${requiredAction}`)).join(", "); html += requiredActions.map(requiredAction => advancedMsgStr(`requiredAction.${requiredAction}`)).join(", ");

View File

@ -1,4 +1,4 @@
import { Fragment } from "react"; import { Fragment, useState } from "react";
import { getKcClsx } from "keycloakify/login/lib/kcClsx"; import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import { kcSanitize } from "keycloakify/lib/kcSanitize"; import { kcSanitize } from "keycloakify/lib/kcSanitize";
import type { PageProps } from "keycloakify/login/pages/PageProps"; import type { PageProps } from "keycloakify/login/pages/PageProps";
@ -17,6 +17,8 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
const { msg, msgStr } = i18n; const { msg, msgStr } = i18n;
const [isSubmitting, setIsSubmitting] = useState(false);
return ( return (
<Template <Template
kcContext={kcContext} kcContext={kcContext}
@ -26,7 +28,16 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
displayMessage={!messagesPerField.existsError("totp")} displayMessage={!messagesPerField.existsError("totp")}
headerNode={msg("doLogIn")} headerNode={msg("doLogIn")}
> >
<form id="kc-otp-login-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post"> <form
id="kc-otp-login-form"
className={kcClsx("kcFormClass")}
action={url.loginAction}
onSubmit={() => {
setIsSubmitting(true);
return true;
}}
method="post"
>
{otpLogin.userOtpCredentials.length > 1 && ( {otpLogin.userOtpCredentials.length > 1 && (
<div className={kcClsx("kcFormGroupClass")}> <div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcInputWrapperClass")}> <div className={kcClsx("kcInputWrapperClass")}>
@ -94,6 +105,7 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
id="kc-login" id="kc-login"
type="submit" type="submit"
value={msgStr("doLogIn")} value={msgStr("doLogIn")}
disabled={isSubmitting}
/> />
</div> </div>
</div> </div>

View File

@ -46,7 +46,7 @@ export const WithRequiredActions: Story = {
kcContext={{ kcContext={{
messageHeader: "Message header", messageHeader: "Message header",
message: { message: {
summary: "Required actions: " summary: "Required actions:"
}, },
requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"], requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"],
"x-keycloakify": { "x-keycloakify": {

View File

@ -62,3 +62,22 @@ export const WithPasswordConfirmError: Story = {
/> />
) )
}; };
/**
* WithAppInitiatedAction:
* - Purpose: Tests when the update password action was triggered by an app.
* - Scenario: Simulates the case where the user presses a 'change password' button in an app and is redirected to Keycloak to change it.
* - Key Aspect: Ensures the 'Cancel' button is shown correctly, which displays only when the action is app initiated.
*/
export const WithAppInitiatedAction: Story = {
render: () => (
<KcPageStory
kcContext={{
url: {
loginAction: "/mock-login-action"
},
isAppInitiatedAction: true
}}
/>
)
};