Compare commits

..

38 Commits

Author SHA1 Message Date
b0819314a1 Update changelog v0.3.20 2021-04-01 22:37:41 +00:00
0099442543 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 23:43:14 +02:00
66a0b07228 Bump version (changelog ignore) 2021-04-01 23:43:08 +02:00
85f9544754 Always catch freemarker errors 2021-04-01 23:42:31 +02:00
2f16a09ab8 Update changelog v0.3.19 2021-04-01 15:21:10 +00:00
183ae98c30 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 17:16:01 +02:00
ba15e63879 Bump version (changelog ignore) 2021-04-01 17:15:55 +02:00
654277feda Fix previous release 2021-04-01 17:15:28 +02:00
81279a5cc5 Update changelog v0.3.18 2021-04-01 15:04:08 +00:00
59f0a843b0 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 16:52:53 +02:00
c094f70171 Bump version (changelog ignore) 2021-04-01 16:51:50 +02:00
0858fe6319 Fix error.ftt, Adopt best effort strategy to convert ftl values into JS 2021-04-01 16:51:28 +02:00
5012ec0ccc Update changelog v0.3.17 2021-03-29 04:33:07 +00:00
990a24fab2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-29 06:27:19 +02:00
036b6bf82a Use push instead of replace in keycloak-js adapter to enable going back 2021-03-29 06:27:12 +02:00
8272a02b52 Update changelog v0.3.15 2021-03-28 12:18:37 +00:00
e346b1d9d2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-28 14:14:52 +02:00
2309bd21c6 Remove all reference to --external-assets, broken feature 2021-03-28 14:14:43 +02:00
7d6476c1b5 Update changelog v0.3.14 2021-03-28 11:39:36 +00:00
e892a0e7e6 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-28 13:37:07 +02:00
ca5b41e730 Fix standalone mode: imports from js 2021-03-28 13:37:02 +02:00
9b18234112 Update changelog v0.3.13 2021-03-26 16:54:38 +00:00
5274368f47 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 17:49:36 +01:00
1415c24028 Bump version (changelog ignore) 2021-03-26 17:49:31 +01:00
4a084f5859 Fix previous release (changelog ignore) 2021-03-26 17:49:09 +01:00
a30c9eb0cd Update changelog v0.3.12 2021-03-26 16:35:41 +00:00
85d3b40b8e Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 17:31:06 +01:00
335afec230 Bump version (changelog ignore) 2021-03-26 17:31:01 +01:00
69fa49848a Fix mocksContext 2021-03-26 17:30:04 +01:00
7a09051127 Update changelog v0.3.11 2021-03-26 14:36:48 +00:00
07ee0ecb8b Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 15:29:23 +01:00
6f133428f8 Fix previous build, improve README 2021-03-26 15:29:17 +01:00
4f733736db fmt (changelog ignore) 2021-03-26 14:06:14 +01:00
d96ff13a67 Update changelog v0.3.10 2021-03-26 13:05:25 +00:00
2c1351ce47 fmt (changelog ignore) 2021-03-26 14:04:45 +01:00
96cd56ec77 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 14:02:46 +01:00
e5b2096d65 Bump version (changelog ignore) 2021-03-26 14:02:40 +01:00
3aa140335f Handle <style> tag, improve documentation 2021-03-26 14:02:14 +01:00
21 changed files with 764 additions and 379 deletions

View File

@ -1,3 +1,43 @@
### **0.3.20** (2021-04-01)
- Always catch freemarker errors
### **0.3.19** (2021-04-01)
- Fix previous release
### **0.3.18** (2021-04-01)
- Fix error.ftt, Adopt best effort strategy to convert ftl values into JS
### **0.3.17** (2021-03-29)
- Use push instead of replace in keycloak-js adapter to enable going back
### **0.3.15** (2021-03-28)
- Remove all reference to --external-assets, broken feature
### **0.3.14** (2021-03-28)
- Fix standalone mode: imports from js
### **0.3.13** (2021-03-26)
### **0.3.12** (2021-03-26)
- Fix mocksContext
### **0.3.11** (2021-03-26)
- Fix previous build, improve README
### **0.3.10** (2021-03-26)
- Handle <style> tag, improve documentation
### **0.3.9** (2021-03-25) ### **0.3.9** (2021-03-25)
- Update readme - Update readme

View File

@ -41,7 +41,6 @@ Here is `yarn add keycloakify` for you 🍸
- [Motivations](#motivations) - [Motivations](#motivations)
- [How to use](#how-to-use) - [How to use](#how-to-use)
- [Setting up the build tool](#setting-up-the-build-tool) - [Setting up the build tool](#setting-up-the-build-tool)
- [Specify from where the resources should be downloaded.](#specify-from-where-the-resources-should-be-downloaded)
- [Developing your login and register pages in your React app](#developing-your-login-and-register-pages-in-your-react-app) - [Developing your login and register pages in your React app](#developing-your-login-and-register-pages-in-your-react-app)
- [Just changing the look](#just-changing-the-look) - [Just changing the look](#just-changing-the-look)
- [Changing the look **and** feel](#changing-the-look-and-feel) - [Changing the look **and** feel](#changing-the-look-and-feel)
@ -49,9 +48,13 @@ Here is `yarn add keycloakify` for you 🍸
- [GitHub Actions](#github-actions) - [GitHub Actions](#github-actions)
- [Requirements](#requirements) - [Requirements](#requirements)
- [Limitations](#limitations) - [Limitations](#limitations)
- [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
- [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-thesrc-dir)
- [Example of setup that **won't** work](#example-of-setup-that-wont-work)
- [Workarounds](#workarounds)
- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [API Reference](#api-reference) - [API Reference](#api-reference)
- [The build tool](#the-build-tool) - [The build tool](#the-build-tool)
- [Implement context persistence (optional)](#implement-context-persistence-optional)
# How to use # How to use
## Setting up the build tool ## Setting up the build tool
@ -66,6 +69,8 @@ Here is `yarn add keycloakify` for you 🍸
"keycloak": "yarn build && build-keycloak-theme", "keycloak": "yarn build && build-keycloak-theme",
}, },
``` ```
`"homepage"` must be specified only if the url path is not `/`
(Onl `/YOUR-APP` matters `URL.OF` don't have to be the actual domain)
It is mandatory that you specify the url where your app will be available It is mandatory that you specify the url where your app will be available
using the `homepage` field. using the `homepage` field.
@ -77,33 +82,6 @@ dependency with `yarn install` and build the keycloak theme with
Once the build is complete instructions about how to load Once the build is complete instructions about how to load
the theme into Keycloak are printed in the console. the theme into Keycloak are printed in the console.
### Specify from where the resources should be downloaded.
When you run `npx build-keycloak-theme` without arguments, Keycloakify will build
a standalone version of the Keycloak theme. That is to say even if your app, the
one hosted at the url specified as `homepage` in your package.json, is down the
Keycloak theme will still work.
In this mode (the default) every asset are served by the keycloak server. It is
convergent for debugging but it production you probably want the assets to be
fetched from your app.
Indeed in the default mode your users have to download again the whole app just
to access the login page. You probably have [long-term asset caching](https://create-react-app.dev/docs/production-build/#static-file-caching)
enabled in the server that host your app ([example](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/nginx.conf#L14))
so it's better if only the html is served by the Keycloak server and everything
else, your JS bundles, your CSS ect point to your app.
To enable this behavior you car run:
```bash
npx build-keycloak-theme --external-assets
```
(instead of `npx build-keycloak-theme`)
This is something you probably want to do in your CI pipeline. [Example](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/.github/workflows/ci.yaml#L112)
Also note that there is [a same-origin policy exception for fonts](https://en.wikipedia.org/wiki/Same-origin_policy#cite_note-3) so you must enabled
CORS for fonts on the server hosting your app. Concretely this mean that your server should add a `Access-Control-Allow-Origin: *` response header to
GET request on *.woff2?. [Example with Nginx](https://github.com/garronej/keycloakify-demo-app/blob/224c43383548635a463fa68e8909c147ac189f0e/nginx.conf#L18-L20)
## Developing your login and register pages in your React app ## Developing your login and register pages in your React app
### Just changing the look ### Just changing the look
@ -214,19 +192,25 @@ NOTE: This build tool has only be tested on MacOS.
# Limitations # Limitations
In the standalone mode (when you run `npx build-keycloak-theme` without `--external-assets`) the fonts won't work if you are self ## `process.env.PUBLIC_URL` not supported.
hosting them. This, for example, wont work: [`src: url("/assets/worksans-bold-webfont.woff2") format("woff2")`](https://github.com/InseeFrLab/onyxia-ui/blob/b24df3a9b34b505ce00619bb8ec0174223ecfaca/src/app/theme/fonts.scss#L5-L6)
you will have to [host them externally](https://github.com/InseeFrLab/onyxia-ui/blob/43bf4a508419072a4ae202698e59d20b69feb9c0/src/app/theme/fonts.scss#L8-L9)
on a server that has CORS enabled.
Again this apply ony if you are not building your theme with `--external-assets` which is advised against in production.
# API Reference
## The build tool You won't be able to [import things from your public directory in your JavaScript code](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system). (This isn't recommended anyway).
Part of the lib that runs with node, at build time. ## `@font-face` importing fonts from the `src/` dir
### Example of setup that **won't** work
- `npx build-keycloak-theme [--external-assets]`: Builds the theme, the CWD is assumed to be the root of your react project. - We have a `fonts/` directory in `src/`
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes) - We import the font like this [`src: url("/fonts/my-font.woff2") format("woff2");`(https://github.com/garronej/keycloakify-demo-app/blob/07d54a3012ef354ee12b1374c6f7ad1cb125d56b/src/fonts.scss#L4) in a `.scss` a file.
### Workarounds
If it is possible, use Google Fonts or any other font provider.
If you want to host your font recommended approach is to move your fonts into the `public`
directory and to place your `@font-face` statements in the `public/index.html`.
Example [here]().
You can also [use your explicit url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
# Implement context persistence (optional) # Implement context persistence (optional)
@ -286,3 +270,12 @@ keycloakInstance.init({
If you really want to go the extra miles and avoid having the white If you really want to go the extra miles and avoid having the white
flash of the blank html before the js bundle have been evaluated flash of the blank html before the js bundle have been evaluated
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`. [here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
# API Reference
## The build tool
Part of the lib that runs with node, at build time.
- `npx build-keycloak-theme`: Builds the theme, the CWD is assumed to be the root of your react project.
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)

View File

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "0.3.9", "version": "0.3.20",
"description": "Keycloak theme generator for Reacts app", "description": "Keycloak theme generator for Reacts app",
"repository": { "repository": {
"type": "git", "type": "git",
@ -14,8 +14,7 @@
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js", "grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
"test": "node dist/test", "test": "node dist/test",
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/", "copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
"generate-messages": "node dist/bin/generate-i18n-messages.js", "generate-messages": "node dist/bin/generate-i18n-messages.js"
"watch": "tsc -w"
}, },
"bin": { "bin": {
"build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js", "build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js",
@ -54,8 +53,8 @@
"evt": "2.0.0-beta.15", "evt": "2.0.0-beta.15",
"minimal-polyfills": "^2.1.6", "minimal-polyfills": "^2.1.6",
"path": "^0.12.7", "path": "^0.12.7",
"powerhooks": "^0.0.27", "powerhooks": "^0.0.32",
"scripting-tools": "^0.19.13", "scripting-tools": "^0.19.13",
"tss-react": "^0.0.11" "tss-react": "^0.0.12"
} }
} }

View File

@ -0,0 +1,263 @@
<script>const _=
{
"url": {
"loginAction": (function (){
<#attempt>
return "${url.loginAction?no_esc}";
<#recover>
</#attempt>
})(),
"resourcesPath": (function (){
<#attempt>
return "${url.resourcesPath?no_esc}";
<#recover>
</#attempt>
})(),
"resourcesCommonPath": (function (){
<#attempt>
return "${url.resourcesCommonPath?no_esc}";
<#recover>
</#attempt>
})(),
"loginRestartFlowUrl": (function (){
<#attempt>
return "${url.loginRestartFlowUrl?no_esc}";
<#recover>
</#attempt>
})(),
"loginUrl": (function (){
<#attempt>
return "${url.loginUrl?no_esc}";
<#recover>
</#attempt>
})()
},
"realm": {
"displayName": (function (){
<#attempt>
return "${realm.displayName!''}" || undefined;
<#recover>
</#attempt>
})(),
"displayNameHtml": (function (){
<#attempt>
return "${realm.displayNameHtml!''}" || undefined;
<#recover>
</#attempt>
})(),
"internationalizationEnabled": (function (){
<#attempt>
return ${realm.internationalizationEnabled?c};
<#recover>
</#attempt>
})(),
"registrationEmailAsUsername": (function (){
<#attempt>
return ${realm.registrationEmailAsUsername?c};
<#recover>
</#attempt>
})()
},
"locale": (function (){
<#attempt>
<#if realm.internationalizationEnabled>
return {
"supported": (function(){
var out= [];
<#attempt>
<#list locale.supported as lng>
out.push({
"url": (function (){
<#attempt>
return "${lng.url?no_esc}";
<#recover>
</#attempt>
})(),
"label": (function (){
<#attempt>
return "${lng.label}";
<#recover>
</#attempt>
})(),
"languageTag": (function (){
<#attempt>
return "${lng.languageTag}";
<#recover>
</#attempt>
})()
});
</#list>
<#recover>
</#attempt>
return out;
})(),
"current": (function (){
<#attempt>
return "${locale.current}";
<#recover>
</#attempt>
})()
};
</#if>
<#recover>
</#attempt>
})(),
"auth": (function (){
<#attempt>
<#if auth?has_content>
var out= {
"showUsername": (function (){
<#attempt>
return ${auth.showUsername()?c};
<#recover>
</#attempt>
})(),
"showResetCredentials": (function (){
<#attempt>
return ${auth.showResetCredentials()?c};
<#recover>
</#attempt>
})(),
"showTryAnotherWayLink": (function(){
<#attempt>
return ${auth.showTryAnotherWayLink()?c};
<#recover>
</#attempt>
})()
};
<#attempt>
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": (function (){
<#attempt>
return "${auth.attemptedUsername}";
<#recover>
</#attempt>
})()
}
);
</#if>
<#recover>
</#attempt>
return out;
</#if>
<#recover>
</#attempt>
})(),
"scripts": (function(){
var out = [];
<#attempt>
<#if scripts??>
<#attempt>
<#list scripts as script>
out.push((function (){
<#attempt>
return "${script}";
<#recover>
</#attempt>
})());
</#list>
<#recover>
</#attempt>
</#if>
<#recover>
</#attempt>
return out;
})(),
"message": (function (){
<#attempt>
<#if message?has_content>
return { 
"type": (function (){
<#attempt>
return "${message.type}";
<#recover>
</#attempt>
})(),
"summary": (function (){
<#attempt>
return String.htmlUnescape("${message.summary}");
<#recover>
</#attempt>
})()
};
</#if>
<#recover>
</#attempt>
})(),
"isAppInitiatedAction": (function (){
<#attempt>
<#if isAppInitiatedAction??>
return true;
</#if>
<#recover>
</#attempt>
return false;
})()
}
</script>

View File

@ -2,13 +2,21 @@
{ {
"client": (function (){ "client": (function (){
<#attempt>
<#if client??> <#if client??>
return { return {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined "baseUrl": (function (){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
}; };
</#if> </#if>
<#recover>
return undefined; </#attempt>
})() })()
} }

View File

@ -2,7 +2,8 @@
import cheerio from "cheerio"; import cheerio from "cheerio";
import { import {
replaceImportFromStaticInJsCode, replaceImportsFromStaticInJsCode,
replaceImportsInInlineCssCode,
generateCssCodeToDefineGlobals generateCssCodeToDefineGlobals
} from "../replaceImportFromStatic"; } from "../replaceImportFromStatic";
import fs from "fs"; import fs from "fs";
@ -18,7 +19,7 @@ function loadAdjacentFile(fileBasename: string) {
.toString("utf8"); .toString("utf8");
}; };
function loadFtlFile(ftlFileBasename: PageId | "template.ftl") { function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
try { try {
return loadAdjacentFile(ftlFileBasename) return loadAdjacentFile(ftlFileBasename)
@ -29,40 +30,42 @@ function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
} }
} }
export type Mode = {
type: "standalone";
urlPathname: string;
} | {
type: "external assets";
urlPathname: string;
urlOrigin: string;
}
export function generateFtlFilesCodeFactory( export function generateFtlFilesCodeFactory(
params: { params: {
ftlValuesGlobalName: string; ftlValuesGlobalName: string;
cssGlobalsToDefine: Record<string, string>; cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string; indexHtmlCode: string;
mode: Mode; urlPathname: string;
} }
) { ) {
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, mode } = params; const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, urlPathname } = params;
const $ = cheerio.load(indexHtmlCode); const $ = cheerio.load(indexHtmlCode);
$("script:not([src])").each((...[, element]) => { $("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportFromStaticInJsCode({ const { fixedJsCode } = replaceImportsFromStaticInJsCode({
ftlValuesGlobalName, ftlValuesGlobalName,
"jsCode": $(element).html()!, "jsCode": $(element).html()!
mode
}); });
$(element).text(fixedJsCode); $(element).text(fixedJsCode);
}); });
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
"urlPathname": params.urlPathname
});
$(element).text(fixedCssCode);
});
([ ([
["link", "href"], ["link", "href"],
["script", "src"], ["script", "src"],
@ -75,31 +78,20 @@ export function generateFtlFilesCodeFactory(
return; return;
} }
switch (mode.type) {
case "external assets":
$(element).attr(
attrName,
href.replace(/^\//, `${mode.urlOrigin}/`)
);
break;
case "standalone":
$(element).attr( $(element).attr(
attrName, attrName,
href.replace( href.replace(
new RegExp(`^${mode.urlPathname.replace(/\//g, "\\/")}`), new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
"${url.resourcesPath}/build/" "${url.resourcesPath}/build/"
) )
); );
break;
}
}) })
); );
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later. //FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const ftlCommonPlaceholders = { const ftlCommonPlaceholders = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("template.ftl"), '{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->': '<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
[ [
'<#if scripts??>', '<#if scripts??>',
@ -119,7 +111,7 @@ export function generateFtlFilesCodeFactory(
'<style>', '<style>',
generateCssCodeToDefineGlobals({ generateCssCodeToDefineGlobals({
cssGlobalsToDefine, cssGlobalsToDefine,
"urlPathname": mode.urlPathname urlPathname
}).cssCodeToPrependInHead, }).cssCodeToPrependInHead,
'</style>', '</style>',
'' ''

View File

@ -1,37 +1,82 @@
<script>const _= <script>const _=
{ {
"messageHeader": "${messageHeader!''}" || undefined, "messageHeader": (function (){
<#attempt>
return "${messageHeader!''}" || undefined;
<#recover>
</#attempt>
})(),
"requiredActions": (function (){ "requiredActions": (function (){
<#attempt>
<#if requiredActions??> <#if requiredActions??>
var out =[]; var out =[];
<#attempt>
<#list requiredActions> <#list requiredActions>
<#attempt>
<#items as reqActionItem> <#items as reqActionItem>
out.push("${reqActionItem}"); out.push((function (){
</#items></b>
<#attempt>
return "${reqActionItem}";
<#recover>
</#attempt>
})());
</#items>
<#recover>
</#attempt>
</#list> </#list>
<#recover>
</#attempt>
return out; return out;
<#else> </#if>
<#recover>
return undefined; </#attempt>
})(), })(),
"skipLink": (function (){ "skipLink": (function (){
<#attempt>
<#if skipLink??> <#if skipLink??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"pageRedirectUri": "${(pageRedirectUri!'')?no_esc}" || undefined, "pageRedirectUri": (function (){
"actionUri": "${(actionUri!'')?no_esc}" || undefined,
<#attempt>
return "${(pageRedirectUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"actionUri": (function (){
<#attempt>
return "${(actionUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"client": { "client": {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined "baseUrl": (function(){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
} }
} }
</script> </script>

View File

@ -1,7 +1,14 @@
<script>const _= <script>const _=
{ {
"realm": { "realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c} "loginWithEmailAllowed": (function (){
},
<#attempt>
return ${realm.loginWithEmailAllowed?c};
<#recover>
</#attempt>
})()
}
} }
</script> </script>

View File

@ -1,83 +1,160 @@
<script>const _= <script>const _=
{ {
"url": { "url": {
"loginResetCredentialsUrl": "${url.loginResetCredentialsUrl?no_esc}", "loginResetCredentialsUrl": (function (){
"registrationUrl": "${url.registrationUrl?no_esc}" <#attempt>
return "${url.loginResetCredentialsUrl?no_esc}";
<#recover>
</#attempt>
})(),
"registrationUrl": (function (){
<#attempt>
return "${url.registrationUrl?no_esc}";
<#recover>
</#attempt>
})()
}, },
"realm": { "realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c}, "loginWithEmailAllowed": (function(){
"rememberMe": ${realm.rememberMe?c}, <#attempt>
"password": ${realm.password?c}, return ${realm.loginWithEmailAllowed?c};
"resetPasswordAllowed": ${realm.resetPasswordAllowed?c}, <#recover>
"registrationAllowed": ${realm.registrationAllowed?c} </#attempt>
})(),
"rememberMe": (function (){
<#attempt>
return ${realm.rememberMe?c};
<#recover>
</#attempt>
})(),
"password": (function (){
<#attempt>
return ${realm.password?c};
<#recover>
</#attempt>
})(),
"resetPasswordAllowed": (function (){
<#attempt>
return ${realm.resetPasswordAllowed?c};
<#recover>
</#attempt>
})(),
"registrationAllowed": (function (){
<#attempt>
return ${realm.registrationAllowed?c};
<#recover>
</#attempt>
})()
}, },
"auth": (function (){ "auth": (function (){
<#attempt>
<#if auth?has_content> <#if auth?has_content>
var out= { return {
"selectedCredential": "${auth.selectedCredential!''}" || undefined "selectedCredential": (function (){
<#attempt>
return "${auth.selectedCredential!''}" || undefined;
<#recover>
</#attempt>
})()
}; };
return out;
</#if> </#if>
<#recover>
return undefined; </#attempt>
})(), })(),
"social": { "social": {
"displayInfo": ${social.displayInfo?c}, "displayInfo": (function (){
<#attempt>
return ${social.displayInfo?c};
<#recover>
</#attempt>
})(),
"providers": (()=>{ "providers": (()=>{
<#attempt>
<#if social.providers??> <#if social.providers??>
var out= []; var out= [];
<#attempt>
<#list social.providers as p> <#list social.providers as p>
out.push({ out.push({
"loginUrl": "${p.loginUrl?no_esc}", "loginUrl": (function (){
"alias": "${p.alias}", <#attempt>
"providerId": "${p.providerId}", return "${p.loginUrl?no_esc}";
"displayName": "${p.displayName}" <#recover>
</#attempt>
})(),
"alias": (function (){
<#attempt>
return "${p.alias}";
<#recover>
</#attempt>
})(),
"providerId": (function (){
<#attempt>
return "${p.providerId}";
<#recover>
</#attempt>
})(),
"displayName": (function (){
<#attempt>
return "${p.displayName}";
<#recover>
</#attempt>
})()
}); });
</#list> </#list>
<#recover>
</#attempt>
return out; return out;
</#if> </#if>
<#recover>
return undefined; </#attempt>
})() })()
}, },
"usernameEditDisabled": (function () { "usernameEditDisabled": (function () {
<#attempt>
<#if usernameEditDisabled??> <#if usernameEditDisabled??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"login": { "login": {
"username": "${login.username!''}" || undefined, "username": (function (){
<#attempt>
return "${login.username!''}" || undefined;
<#recover>
</#attempt>
})(),
"rememberMe": (function (){ "rememberMe": (function (){
<#attempt>
<#if login.rememberMe??> <#if login.rememberMe??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})() })()
}, },
"registrationDisabled": (function (){ "registrationDisabled": (function (){
<#attempt>
<#if registrationDisabled??> <#if registrationDisabled??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})() })()
} }
</script> </script>

View File

@ -1,46 +1,152 @@
<script>const _= <script>const _=
{ {
"url": { "url": {
"registrationAction": "${url.registrationAction?no_esc}" "registrationAction": (function (){
<#attempt>
return "${url.registrationAction?no_esc}";
<#recover>
</#attempt>
})()
}, },
"messagesPerField": { "messagesPerField": {
"printIfExists": function (key, x) { "printIfExists": function (key, x) {
switch(key){ switch(key){
case "userLabel": "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined; case "userLabel": return (function (){
case "username": "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
case "email": "${messagesPerField.printIfExists('email','1')}" ? x : undefined; <#attempt>
case "firstName": "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined; return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
case "lastName": "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined; <#recover>
case "password": "${messagesPerField.printIfExists('password','1')}" ? x : undefined; </#attempt>
case "password-confirm": "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
})();
case "username": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "email": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "firstName": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "lastName": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "password": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "password-confirm": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
} }
} }
}, },
"register": { "register": {
"formData": { "formData": {
"firstName": "${register.formData.firstName!''}" || undefined, "firstName": (function (){
"displayName": "${register.formData.displayName!''}" || undefined,
"lastName": "${register.formData.lastName!''}" || undefined, <#attempt>
"email": "${register.formData.email!''}" || undefined, return "${register.formData.firstName!''}" || undefined;
"username": "${register.formData.username!''}" || undefined <#recover>
</#attempt>
})(),
"displayName": (function (){
<#attempt>
return "${register.formData.displayName!''}" || undefined;
<#recover>
</#attempt>
})(),
"lastName": (function (){
<#attempt>
return "${register.formData.lastName!''}" || undefined;
<#recover>
</#attempt>
})(),
"email": (function(){
<#attempt>
return "${register.formData.email!''}" || undefined;
<#recover>
</#attempt>
})(),
"username": (function (){
<#attempt>
return "${register.formData.username!''}" || undefined;
<#recover>
</#attempt>
})()
} }
}, },
"passwordRequired": (function (){ "passwordRequired": (function (){
<#attempt>
<#if passwordRequired??> <#if passwordRequired??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"recaptchaRequired": (function (){ "recaptchaRequired": (function (){
<#attempt>
<#if passwordRequired??> <#if passwordRequired??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"recaptchaSiteKey": "${recaptchaSiteKey!''}" || undefined "recaptchaSiteKey": (function (){
<#attempt>
return "${recaptchaSiteKey!''}" || undefined;
<#recover>
</#attempt>
})()
} }
</script> </script>

View File

@ -1,114 +0,0 @@
<script>const _=
{
"url": {
"loginAction": "${url.loginAction?no_esc}",
"resourcesPath": "${url.resourcesPath?no_esc}",
"resourcesCommonPath": "${url.resourcesCommonPath?no_esc}",
"loginRestartFlowUrl": "${url.loginRestartFlowUrl?no_esc}",
"loginUrl": "${url.loginUrl?no_esc}"
},
"realm": {
"displayName": "${realm.displayName!''}" || undefined,
"displayNameHtml": "${realm.displayNameHtml!''}" || undefined,
"internationalizationEnabled": ${realm.internationalizationEnabled?c},
"registrationEmailAsUsername": ${realm.registrationEmailAsUsername?c},
},
"locale": (function (){
<#if realm.internationalizationEnabled>
return {
"supported": (function(){
<#if realm.internationalizationEnabled>
var out= [];
<#list locale.supported as lng>
out.push({
"url": "${lng.url?no_esc}",
"label": "${lng.label}",
"languageTag": "${lng.languageTag}"
});
</#list>
return out;
</#if>
return undefined;
})(),
"current": "${locale.current}"
};
</#if>
return undefined;
})(),
"auth": (function (){
<#if auth?has_content>
var out= {
"showUsername": ${auth.showUsername()?c},
"showResetCredentials": ${auth.showResetCredentials()?c},
"showTryAnotherWayLink": ${auth.showTryAnotherWayLink()?c},
};
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": "${auth.attemptedUsername}"
}
);
</#if>
return out;
</#if>
return undefined;
})(),
"scripts": (function(){
var out = [];
<#if scripts??>
<#list scripts as script>
out.push("${script}");
</#list>
</#if>
return out;
})(),
"message": (function (){
<#if message?has_content>
return { 
"type": "${message.type}",
"summary": String.htmlUnescape("${message.summary}")
};
</#if>
return undefined;
})(),
"isAppInitiatedAction": (function (){
<#if isAppInitiatedAction??>
return true;
</#if>
return false;
})()
}
</script>

View File

@ -3,10 +3,10 @@ import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs"; import * as fs from "fs";
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { import {
replaceImportFromStaticInCssCode, replaceImportsInCssCode,
replaceImportFromStaticInJsCode replaceImportsFromStaticInJsCode
} from "./replaceImportFromStatic"; } from "./replaceImportFromStatic";
import { generateFtlFilesCodeFactory, pageIds, Mode } from "./generateFtl"; import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes"; import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip"; import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import * as child_process from "child_process"; import * as child_process from "child_process";
@ -20,11 +20,11 @@ export function generateKeycloakThemeResources(
themeName: string; themeName: string;
reactAppBuildDirPath: string; reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string; keycloakThemeBuildingDirPath: string;
mode: Mode; urlPathname: string;
} }
) { ) {
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, mode } = params; const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, urlPathname } = params;
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login"); const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
@ -45,11 +45,10 @@ export function generateKeycloakThemeResources(
return undefined; return undefined;
} }
if (mode.type === "standalone") {
if (/\.css?$/i.test(filePath)) { if (/\.css?$/i.test(filePath)) {
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode( const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
{ "cssCode": sourceCode.toString("utf8") } { "cssCode": sourceCode.toString("utf8") }
); );
@ -62,20 +61,18 @@ export function generateKeycloakThemeResources(
} }
if (/\.js?$/i.test(filePath)) { if (/\.js?$/i.test(filePath)) {
const { fixedJsCode } = replaceImportFromStaticInJsCode({ const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"), "jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName, ftlValuesGlobalName
mode
}); });
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") }; return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
} }
}
return { "modifiedSourceCode": sourceCode }; return { "modifiedSourceCode": sourceCode };
} }
@ -87,7 +84,7 @@ export function generateKeycloakThemeResources(
"indexHtmlCode": fs.readFileSync( "indexHtmlCode": fs.readFileSync(
pathJoin(reactAppBuildDirPath, "index.html") pathJoin(reactAppBuildDirPath, "index.html")
).toString("utf8"), ).toString("utf8"),
mode urlPathname
}); });
pageIds.forEach(pageId => { pageIds.forEach(pageId => {

View File

@ -11,8 +11,6 @@ import { URL } from "url";
const reactProjectDirPath = process.cwd(); const reactProjectDirPath = process.cwd();
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json")); const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak"); export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
@ -26,8 +24,7 @@ if (require.main === module) {
keycloakThemeBuildingDirPath, keycloakThemeBuildingDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"), "reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
"themeName": parsedPackageJson.name, "themeName": parsedPackageJson.name,
"mode": (() => { "urlPathname": (() => {
const url = (() => { const url = (() => {
@ -39,37 +36,12 @@ if (require.main === module) {
})(); })();
const urlPathname = return url === undefined ?
url === undefined ?
"/" : "/" :
url.pathname.replace(/([^/])$/, "$1/"); url.pathname.replace(/([^/])$/, "$1/");
return !doUseExternalAssets ?
{
"type": "standalone",
urlPathname
} as const
:
{
"type": "external assets",
urlPathname,
"urlOrigin": (() => {
if (url === undefined) {
console.error("ERROR: You must specify 'homepage' in your package.json");
process.exit(-1);
}
return url.origin;
})() })()
} as const;
})()
}); });
const { jarFilePath } = generateJavaStackFiles({ const { jarFilePath } = generateJavaStackFiles({

View File

@ -1,45 +1,45 @@
import * as crypto from "crypto"; import * as crypto from "crypto";
type Mode = { export function replaceImportsFromStaticInJsCode(
type: "standalone";
} | {
type: "external assets";
urlOrigin: string;
urlPathname: string;
}
export function replaceImportFromStaticInJsCode(
params: { params: {
ftlValuesGlobalName: string; ftlValuesGlobalName: string;
jsCode: string; jsCode: string;
mode: Mode;
} }
): { fixedJsCode: string; } { ): { fixedJsCode: string; } {
const { jsCode, ftlValuesGlobalName, mode } = params; const { jsCode, ftlValuesGlobalName } = params;
const fixedJsCode = (() => { const fixedJsCode = jsCode.replace(
switch (mode.type) {
case "standalone":
return jsCode!.replace(
/[a-z]+\.[a-z]+\+"static\//g, /[a-z]+\.[a-z]+\+"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
); );
case "external assets":
return jsCode!.replace(
/[a-z]+\.[a-z]+\+"static\//g,
`"${mode.urlOrigin}${mode.urlPathname}static/`
);
}
})();
return { fixedJsCode }; return { fixedJsCode };
} }
export function replaceImportFromStaticInCssCode( export function replaceImportsInInlineCssCode(
params: {
cssCode: string;
urlPathname: string;
}
): { fixedCssCode: string; } {
const { cssCode, urlPathname } = params;
const fixedCssCode = cssCode.replace(
urlPathname === "/" ?
/url\(\/([^/][^)]+)\)/g :
new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
(...[, group]) => `url(${"${url.resourcesPath}/build/" + group})`
);
return { fixedCssCode };
}
export function replaceImportsInCssCode(
params: { params: {
cssCode: string; cssCode: string;
} }
@ -52,7 +52,7 @@ export function replaceImportFromStaticInCssCode(
const cssGlobalsToDefine: Record<string, string> = {}; const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/url\(\/[^)]+\)[^;}]*/g) ?? []) new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
.forEach(match => .forEach(match =>
cssGlobalsToDefine[ cssGlobalsToDefine[
"url" + crypto "url" + crypto

View File

@ -25,7 +25,7 @@ export type TemplateProps = {
showUsernameNode?: ReactNode; showUsernameNode?: ReactNode;
formNode: ReactNode; formNode: ReactNode;
infoNode?: ReactNode; infoNode?: ReactNode;
} & { kcContext: KcContext.Template; } & KcTemplateProps; } & { kcContext: KcContext; } & KcTemplateProps;
export const Template = memo((props: TemplateProps) => { export const Template = memo((props: TemplateProps) => {

View File

@ -10,12 +10,17 @@ import type { LanguageLabel } from "./i18n/KcLanguageTag";
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
StrEnum extends `${Prefix}${infer U}` ? U : never; StrEnum extends `${Prefix}${infer U}` ? U : never;
/** Take theses type definition with a grain of salt.
* Some values might be undefined on some pages.
* (ex: url.loginAction is undefined on error.ftl)
*/
export type KcContext = export type KcContext =
KcContext.Login | KcContext.Register | KcContext.Info | KcContext.Login | KcContext.Register | KcContext.Info |
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail; KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail;
export declare namespace KcContext { export declare namespace KcContext {
export type Template = { export type Common = {
url: { url: {
loginAction: string; loginAction: string;
resourcesPath: string; resourcesPath: string;
@ -27,7 +32,7 @@ export declare namespace KcContext {
displayName?: string; displayName?: string;
displayNameHtml?: string; displayNameHtml?: string;
internationalizationEnabled: boolean; internationalizationEnabled: boolean;
registrationEmailAsUsername: boolean; //<--- registrationEmailAsUsername: boolean;
}; };
/** Undefined if !realm.internationalizationEnabled */ /** Undefined if !realm.internationalizationEnabled */
locale?: { locale?: {
@ -55,7 +60,7 @@ export declare namespace KcContext {
isAppInitiatedAction: boolean; isAppInitiatedAction: boolean;
}; };
export type Login = Template & { export type Login = Common & {
pageId: "login.ftl"; pageId: "login.ftl";
url: { url: {
loginResetCredentialsUrl: string; loginResetCredentialsUrl: string;
@ -88,7 +93,7 @@ export declare namespace KcContext {
}; };
}; };
export type Register = Template & { export type Register = Common & {
pageId: "register.ftl"; pageId: "register.ftl";
url: { url: {
registrationAction: string; registrationAction: string;
@ -120,7 +125,7 @@ export declare namespace KcContext {
recaptchaSiteKey?: string; recaptchaSiteKey?: string;
}; };
export type Info = Template & { export type Info = Common & {
pageId: "info.ftl"; pageId: "info.ftl";
messageHeader?: string; messageHeader?: string;
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[]; requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
@ -132,21 +137,21 @@ export declare namespace KcContext {
} }
}; };
export type Error = Template & { export type Error = Common & {
pageId: "error.ftl"; pageId: "error.ftl";
client?: { client?: {
baseUrl?: string; baseUrl?: string;
} }
}; };
export type LoginResetPassword = Template & { export type LoginResetPassword = Common & {
pageId: "login-reset-password.ftl"; pageId: "login-reset-password.ftl";
realm: { realm: {
loginWithEmailAllowed: boolean; loginWithEmailAllowed: boolean;
} }
}; };
export type LoginVerifyEmail = Template & { export type LoginVerifyEmail = Common & {
pageId: "login-verify-email.ftl"; pageId: "login-verify-email.ftl";
}; };

View File

@ -6,11 +6,11 @@ import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
//NOTE: Aside because we want to be able to import them from node //NOTE: Aside because we want to be able to import them from node
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath"; import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
export const kcTemplateContext: KcContext.Template = { const kcCommonContext: KcContext.Common = {
"url": { "url": {
"loginAction": "#", "loginAction": "#",
"resourcesPath": "/" + resourcesPath, "resourcesPath": `${process.env["PUBLIC_URL"]}/${resourcesPath}`,
"resourcesCommonPath": "/" + resourcesCommonPath, "resourcesCommonPath": `${process.env["PUBLIC_URL"]}/${resourcesCommonPath}`,
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg", "loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg", "loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
}, },
@ -111,7 +111,7 @@ export const kcTemplateContext: KcContext.Template = {
}; };
Object.defineProperty( Object.defineProperty(
kcTemplateContext.locale!, kcCommonContext.locale!,
"current", "current",
{ {
"get": () => getKcLanguageTagLabel(getEvtKcLanguage().state), "get": () => getKcLanguageTagLabel(getEvtKcLanguage().state),
@ -120,22 +120,22 @@ Object.defineProperty(
); );
export const kcLoginContext: KcContext.Login = { export const kcLoginContext: KcContext.Login = {
...kcTemplateContext, ...kcCommonContext,
"pageId": "login.ftl", "pageId": "login.ftl",
"url": { "url": {
...kcTemplateContext.url, ...kcCommonContext.url,
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg", "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"
}, },
"realm": { "realm": {
...kcTemplateContext.realm, ...kcCommonContext.realm,
"loginWithEmailAllowed": true, "loginWithEmailAllowed": true,
"rememberMe": true, "rememberMe": true,
"password": true, "password": true,
"resetPasswordAllowed": true, "resetPasswordAllowed": true,
"registrationAllowed": true "registrationAllowed": true
}, },
"auth": kcTemplateContext.auth!, "auth": kcCommonContext.auth!,
"social": { "social": {
"displayInfo": true "displayInfo": true
}, },
@ -147,7 +147,7 @@ export const kcLoginContext: KcContext.Login = {
}; };
export const kcRegisterContext: KcContext.Register = { export const kcRegisterContext: KcContext.Register = {
...kcTemplateContext, ...kcCommonContext,
"url": { "url": {
...kcLoginContext.url, ...kcLoginContext.url,
"registrationAction": "http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0" "registrationAction": "http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
@ -166,7 +166,7 @@ export const kcRegisterContext: KcContext.Register = {
}; };
export const kcInfoContext: KcContext.Info ={ export const kcInfoContext: KcContext.Info ={
...kcTemplateContext, ...kcCommonContext,
"pageId": "info.ftl", "pageId": "info.ftl",
"messageHeader": "<Message header>", "messageHeader": "<Message header>",
"requiredActions": undefined, "requiredActions": undefined,
@ -178,7 +178,7 @@ export const kcInfoContext: KcContext.Info ={
}; };
export const kcErrorContext: KcContext.Error = { export const kcErrorContext: KcContext.Error = {
...kcTemplateContext, ...kcCommonContext,
"pageId": "error.ftl", "pageId": "error.ftl",
"client": { "client": {
"baseUrl": "#" "baseUrl": "#"
@ -186,16 +186,16 @@ export const kcErrorContext: KcContext.Error = {
}; };
export const kcLoginResetPasswordContext: KcContext.LoginResetPassword = { export const kcLoginResetPasswordContext: KcContext.LoginResetPassword = {
...kcTemplateContext, ...kcCommonContext,
"pageId": "login-reset-password.ftl", "pageId": "login-reset-password.ftl",
"realm":{ "realm":{
...kcTemplateContext.realm, ...kcCommonContext.realm,
"loginWithEmailAllowed": false "loginWithEmailAllowed": false
} }
}; };
export const kcLoginVerifyEmailContext: KcContext.LoginVerifyEmail = { export const kcLoginVerifyEmailContext: KcContext.LoginVerifyEmail = {
...kcTemplateContext, ...kcCommonContext,
"pageId": "login-verify-email.ftl" "pageId": "login-verify-email.ftl"
}; };

View File

@ -66,12 +66,11 @@ export function createKeycloakAdapter(
return { return {
"login": options => { "login": options => {
window.location.replace( window.location.href=
transformUrlBeforeRedirect( transformUrlBeforeRedirect(
keycloakInstance.createLoginUrl( keycloakInstance.createLoginUrl(
options options
) )
)
); );
return neverResolvingPromise; return neverResolvingPromise;
}, },
@ -86,13 +85,13 @@ export function createKeycloakAdapter(
return neverResolvingPromise; return neverResolvingPromise;
}, },
"register": options => { "register": options => {
window.location.replace( window.location.href =
transformUrlBeforeRedirect( transformUrlBeforeRedirect(
keycloakInstance.createRegisterUrl( keycloakInstance.createRegisterUrl(
options options
) )
)
); );
return neverResolvingPromise; return neverResolvingPromise;
}, },
"accountManagement": () => { "accountManagement": () => {

View File

@ -12,9 +12,6 @@ generateKeycloakThemeResources({
"themeName": "keycloakify-demo-app", "themeName": "keycloakify-demo-app",
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"), "reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"), "keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
"mode": {
"type": "standalone",
"urlPathname": "/keycloakify-demo-app/" "urlPathname": "/keycloakify-demo-app/"
}
}); });

View File

@ -1,11 +1,11 @@
import {  import { 
replaceImportFromStaticInJsCode, replaceImportsFromStaticInJsCode,
replaceImportFromStaticInCssCode, replaceImportsInCssCode,
generateCssCodeToDefineGlobals generateCssCodeToDefineGlobals
} from "../bin/build-keycloak-theme/replaceImportFromStatic"; } from "../bin/build-keycloak-theme/replaceImportFromStatic";
const { fixedJsCode } = replaceImportFromStaticInJsCode({ const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"ftlValuesGlobalName": "keycloakFtlValues", "ftlValuesGlobalName": "keycloakFtlValues",
"jsCode": ` "jsCode": `
function f() { function f() {
@ -19,13 +19,12 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
3: "0664cdc0" 3: "0664cdc0"
}[e] + ".chunk.js" }[e] + ".chunk.js"
} }
`, `
"mode": { "type": "standalone" }
}); });
console.log({ fixedJsCode }); console.log({ fixedJsCode });
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({ const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
"cssCode": ` "cssCode": `
.my-div { .my-div {

View File

@ -747,10 +747,10 @@ path@^0.12.7:
process "^0.11.1" process "^0.11.1"
util "^0.10.3" util "^0.10.3"
powerhooks@^0.0.27: powerhooks@^0.0.32:
version "0.0.27" version "0.0.32"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.27.tgz#e9dc29258860d2f6bf32b249d9cba07c6f53f393" resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.32.tgz#a6aa19573b22f38f4f0bb44573c3242116e9eed1"
integrity sha512-ohayWhtIEdLqiC2th/GEhaRfOhqekFg2uFo0JZ8Dn7oTnAZybs618QJeq5ag9oy3lFVzl+kbROpVa8Ch5zrkaA== integrity sha512-atxsb/zcN8O72hqtfz6tv4XLlh4X22+iTO96PPKpa//nrdvX8KTpFoGiUTh+RQ9RONdR4hP/RmfsDu1zPp2G8Q==
dependencies: dependencies:
evt "2.0.0-beta.15" evt "2.0.0-beta.15"
memoizee "^0.4.15" memoizee "^0.4.15"
@ -927,10 +927,10 @@ to-fast-properties@^2.0.0:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tss-react@^0.0.11: tss-react@^0.0.12:
version "0.0.11" version "0.0.12"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.11.tgz#1cd061927744cd4fc9b7346e2fd1cfcf896a18d5" resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
integrity sha512-j8CDpHHIl6S6/mX+AmK08v7waPqwgNA7urHOD3qknCgbY79LRlS7he5DF4NUNE/5B4/Btc3F25w+KqgChNbyGw== integrity sha512-oHekukqdaE71uhHx4XEdHy6aMnDYhoHLWB94iy2Fy9X8btH2lJH1joPj0zS1q7+1Xy2TydkLEZsTq3ElVd7ZqA==
dependencies: dependencies:
"@emotion/css" "^11.1.3" "@emotion/css" "^11.1.3"