Compare commits

...

24 Commits

Author SHA1 Message Date
70e3fb8de6 Update changelog v1.2.0 2021-06-22 10:42:42 +00:00
9cf75da732 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-06-22 12:37:23 +02:00
2cd266caff Generate kcContext automatically 🚀 2021-06-22 12:37:21 +02:00
28036f1da5 Update changelog v1.1.6 2021-06-21 14:04:36 +00:00
0dacf2fe30 Bump version (changelog ignore) 2021-06-21 15:59:44 +02:00
32f5ef5e5c Fix: Alert messages sometimes includes HTML that is not rendered 2021-06-21 15:59:23 +02:00
98d91bbde7 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-06-21 15:58:40 +02:00
7a92a75d83 Update dist 2021-06-21 15:57:22 +02:00
f5556a02fc Update changelog v1.1.5 2021-06-15 15:38:25 +00:00
1050f4d928 Bump version (changelog ignore) 2021-06-15 17:32:26 +02:00
9276b08f4b #11: Provide socials in the register 2021-06-15 17:32:03 +02:00
a501af669c Update changelog v1.1.4 2021-06-15 12:59:00 +00:00
85343fcefe Bump version (changelog ignore) 2021-06-15 14:49:46 +02:00
b11dfde6e6 Merge pull request #12 from InseeFrLab/email-typo
Fix typo on email
2021-06-15 14:38:27 +02:00
38d2108f02 Fix typo on email 2021-06-15 09:47:22 +02:00
2b67544517 Update changelog v1.1.3 2021-06-14 22:36:25 +00:00
0d443ca88e Add missing key in Login for providers 2021-06-15 00:31:20 +02:00
71f7a5819d Update changelog v1.1.2 2021-06-14 21:33:09 +00:00
5f4abee615 Fix previous build (changelog ignore) 2021-06-14 23:27:15 +02:00
f5ee949006 Fix CI (changelog ignore) 2021-06-14 23:15:41 +02:00
7e85085558 Bump version (changelog ingore) 2021-06-14 23:10:53 +02:00
55a0b27f16 Fix previous build (changelog ignore) 2021-06-14 23:10:35 +02:00
eb0e814f94 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-06-14 22:41:29 +02:00
b7fe20c5a5 Update CI (changelog ignore) 2021-06-14 22:41:22 +02:00
23 changed files with 268 additions and 917 deletions

View File

@ -13,7 +13,7 @@ jobs:
runs-on: macos-10.15
strategy:
matrix:
node: [ '14', '13', '12' ]
node: [ '15', '14', '13' ]
name: Test with Node v${{ matrix.node }}
steps:
- name: Tell if project is using npm or yarn
@ -25,14 +25,13 @@ jobs:
- uses: actions/setup-node@v2.1.3
with:
node-version: ${{ matrix.node }}
- uses: bahmutov/npm-install@v1
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
run: |
yarn install --frozen-lockfile
yarn build
yarn test
- if: steps.step1.outputs.npm_or_yarn == 'npm'
run: |
npm ci
npm run build
npm test
check_if_version_upgraded:
@ -55,11 +54,10 @@ jobs:
needs: check_if_version_upgraded
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
steps:
- uses: garronej/github_actions_toolkit@v2.2
- uses: garronej/github_actions_toolkit@v2.4
with:
action_name: update_changelog
branch: ${{ github.ref }}
commit_author_email: ts_ci@github.com
create_github_release:
runs-on: ubuntu-latest
@ -104,16 +102,13 @@ jobs:
- uses: actions/setup-node@v2.1.3
with:
node-version: '15'
registry-url: https://registry.npmjs.org/
- uses: bahmutov/npm-install@v1
- run: |
PACKAGE_MANAGER=npm
if [ -f "./yarn.lock" ]; then
PACKAGE_MANAGER=yarn
fi
if [ "$PACKAGE_MANAGER" = "yarn" ]; then
yarn install --frozen-lockfile
else
npm ci
fi
$PACKAGE_MANAGER run build
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
env:
@ -124,13 +119,11 @@ jobs:
echo "This version is already published"
exit 0
fi
if [ "$NPM_TOKEN" = "" ]; then
if [ "$NODE_AUTH_TOKEN" = "" ]; then
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
false
fi
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
npm publish
rm .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}

View File

@ -1,3 +1,31 @@
## **1.2.0** (2021-06-22)
- Generate kcContext automatically :rocket:
### **1.1.6** (2021-06-21)
- Fix: Alert messages sometimes includes HTML that is not rendered
- Update dist
### **1.1.5** (2021-06-15)
- #11: Provide socials in the register
### **1.1.4** (2021-06-15)
- Merge pull request #12 from InseeFrLab/email-typo
Fix typo on email
- Fix typo on email
### **1.1.3** (2021-06-14)
- Add missing key in Login for providers
### **1.1.2** (2021-06-14)
### **1.1.1** (2021-06-14)

View File

@ -271,6 +271,10 @@ Then to load your own therms of services using [like this](https://github.com/ga
# Some pages still have the default theme. Why?
**NEW in v1.2 it is now much more easy to add support for custom pages since the
Keycloak context is now automatically converted into a JavaScript object (kcContext).
In v2 (coming soon) it won't be required to fork for adding support for custom pages.**
This project only support the most common user facing pages of Keycloak login.
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "1.1.1",
"version": "1.2.0",
"description": "Keycloak theme generator for Reacts app",
"repository": {
"type": "git",

View File

@ -15,8 +15,7 @@ const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPat
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
if (require.main === module) {
export function main() {
console.log("🔏 Building the keycloak theme...⌚");

View File

@ -1,26 +0,0 @@
var es = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
var unes = {
'&': '&',
'&': '&',
'&lt;': '<',
'&#60;': '<',
'&gt;': '>',
'&#62;': '>',
'&apos;': "'",
'&#39;': "'",
'&quot;': '"',
'&#34;': '"'
};
var cape = function (m) { return unes[m]; };
Object.defineProperty(
String,
"htmlUnescape",
{
"value": function (un) {
return String.prototype.replace.call(un, es, cape);
}
}
);

View File

@ -1,263 +1,194 @@
<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 (){
<#macro objectToJson object depth>
<@compress>
<#local isHash = false>
<#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>
<#local isHash = object?is_hash || object?is_hash_ex>
<#recover>
/* can't evaluate if object is hash */
undefined
<#return>
</#attempt>
<#if isHash>
})(),
"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>
})()
};
<#local keys = "">
<#attempt>
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": (function (){
<#attempt>
return "${auth.attemptedUsername}";
<#recover>
</#attempt>
})()
}
);
</#if>
<#local keys = object?keys>
<#recover>
/* can't list keys of object */
undefined
<#return>
</#attempt>
return out;
{${'\n'}
/* <#list keys as key> ${key} </#list> */
<#list keys as key>
<#if key == "class">
/* skipping "class" property of object */
<#continue>
</#if>
<#local value = "">
<#attempt>
<#local value = object[key]>
<#recover>
/* couldn't dereference ${key} of object */
<#continue>
</#attempt>
<#if depth gt 4>
/* Avoid calling recustively too many times depth: ${depth}, key: ${key} */
<#continue>
</#if>
"${key}": <@objectToJson object=value depth=depth+1/>,
</#list>
}${'\n'}
<#return>
</#if>
<#local isMethod = "">
<#attempt>
<#local isMethod = object?is_method>
<#recover>
/* can't test if object is a method */
undefined
<#return>
</#attempt>
})(),
"scripts": (function(){
<#if isMethod>
undefined
<#return>
</#if>
<#local isBoolean = "">
<#attempt>
<#local isBoolean = object?is_boolean>
<#recover>
/* can't test if object is a boolean */
undefined
<#return>
</#attempt>
<#if isBoolean>
${object?c}
<#return>
</#if>
<#local isEnumerable = "">
<#attempt>
<#local isEnumerable = object?is_enumerable>
<#recover>
/* can't test if object is enumerable */
undefined
<#return>
</#attempt>
<#if isEnumerable>
[${'\n'}
<#list object as item>
<@objectToJson object=item depth=depth+1/>,
</#list>
]${'\n'}
<#return>
</#if>
var out = [];
<#attempt>
<#if scripts??>
<#attempt>
<#list scripts as script>
out.push((function (){
"${object?no_esc}"
<#recover>
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
undefined;
<#return>
</#attempt>
</@compress>
</#macro>
(()=>{
//Removing all the undefined
const obj = JSON.parse(JSON.stringify(<@objectToJson object=.data_model depth=0 />));
//Freemarker values that can't be automatically converted into a JavaScript object.
Object.deepAssign(
obj,
{
"messagesPerField": {
"printIfExists": function (key, x) {
switch(key){
case "userLabel": return (function (){
<#attempt>
return "${script}";
return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
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>
})();
}
}
},
"msg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); },
}
);
})());
</#list>
<#recover>
</#attempt>
</#if>
<#recover>
</#attempt>
return obj;
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

@ -1,23 +0,0 @@
<script>const _=
{
"client": (function (){
<#attempt>
<#if client??>
return {
"baseUrl": (function (){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
};
</#if>
<#recover>
</#attempt>
})()
}
</script>

View File

@ -12,10 +12,10 @@ import { objectKeys } from "evt/tools/typeSafety/objectKeys";
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
export const pageIds = [
"login.ftl", "register.ftl", "info.ftl",
"error.ftl", "login-reset-password.ftl",
"login.ftl", "register.ftl", "info.ftl",
"error.ftl", "login-reset-password.ftl",
"login-verify-email.ftl", "terms.ftl",
"login-otp.ftl", "login-update-profile.ftl",
"login-otp.ftl", "login-update-profile.ftl",
"login-idp-link-confirm.ftl"
] as const;
@ -26,17 +26,6 @@ function loadAdjacentFile(fileBasename: string) {
.toString("utf8");
};
function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
try {
return loadAdjacentFile(ftlFileBasename)
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1];
} catch {
return "{}";
}
}
export function generateFtlFilesCodeFactory(
params: {
@ -100,8 +89,9 @@ export function generateFtlFilesCodeFactory(
);
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const ftlCommonPlaceholders = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
const ftlPlaceholders = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile("common.ftl")
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
[
'<#if scripts??>',
@ -126,23 +116,19 @@ export function generateFtlFilesCodeFactory(
'</style>',
''
]),
...["Object.deepAssign.js", "String.htmlUnescape.js"].map(
fileBasename => [
"<script>",
loadAdjacentFile(fileBasename),
"</script>"
].join("\n")
),
"<script>",
loadAdjacentFile("Object.deepAssign.js"),
"</script>",
'<script>',
` window.${ftlValuesGlobalName}= Object.assign(`,
` {},`,
` ${objectKeys(ftlCommonPlaceholders)[0]}`,
` ${objectKeys(ftlPlaceholders)[0]}`,
' );',
'</script>',
'',
pageSpecificCodePlaceholder,
'',
objectKeys(ftlCommonPlaceholders)[1]
objectKeys(ftlPlaceholders)[1]
].join("\n"),
);
@ -158,11 +144,6 @@ export function generateFtlFilesCodeFactory(
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
const ftlPlaceholders = {
'{ "x": "kxOlLqMeOed9sdLdIdOxd444" }': loadFtlFile(pageId),
...ftlCommonPlaceholders
};
let ftlCode = $.html()
.replace(
pageSpecificCodePlaceholder,
@ -172,10 +153,6 @@ export function generateFtlFilesCodeFactory(
` window.${ftlValuesGlobalName},`,
` { "pageId": "${pageId}" }`,
' );',
` Object.deepAssign(`,
` window.${ftlValuesGlobalName},`,
` ${objectKeys(ftlPlaceholders)[0]}`,
' );',
'</script>'
].join("\n")
);

View File

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

View File

@ -1,11 +0,0 @@
<script>const _=
{
"idpAlias": (function (){
<#attempt>
return "${idpAlias}";
<#recover>
</#attempt>
return "";
})()
}
</script>

View File

@ -1,37 +0,0 @@
<script>const _=
{
"otpLogin": {
"userOtpCredentials": (function(){
var out = [];
<#attempt>
<#list otpLogin.userOtpCredentials as otpCredential>
out.push({
"id": (function (){
<#attempt>
return "${otpCredential.id}";
<#recover>
</#attempt>
})(),
"userLabel": (function (){
<#attempt>
return "${otpCredential.userLabel}";
<#recover>
</#attempt>
})()
});
</#list>
<#recover>
</#attempt>
return out;
})()
}
}
</script>

View File

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

View File

@ -1,67 +0,0 @@
<script>const _=
{
"user": {
"editUsernameAllowed": (function (){
<#attempt>
return ${user.editUsernameAllowed?c};
<#recover>
</#attempt>
})(),
"username": (function (){
<#attempt>
return "${user.username!''}" || undefined;
<#recover>
</#attempt>
})(),
"emal": (function (){
<#attempt>
return "${user.email!''}" || undefined;
<#recover>
</#attempt>
})(),
"firstName": (function (){
<#attempt>
return "${user.firstName!''}" || undefined;
<#recover>
</#attempt>
})(),
"lastName": (function (){
<#attempt>
return "${user.lastName!''}" || undefined;
<#recover>
</#attempt>
})()
},
"messagesPerField": {
"printIfExists": function (key, x) {
switch(key){
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>
})();
}
}
},
}
</script>

View File

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

View File

@ -1,189 +0,0 @@
<script>const _=
{
"url": {
"registrationAction": (function (){
<#attempt>
return "${url.registrationAction?no_esc}";
<#recover>
</#attempt>
})()
},
"messagesPerField": {
"printIfExists": function (key, x) {
switch(key){
case "userLabel": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
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": {
"formData": {
"firstName": (function (){
<#attempt>
return "${register.formData.firstName!''}" || 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 (){
<#attempt>
<#if passwordRequired??>
return true;
</#if>
<#recover>
</#attempt>
return false;
})(),
"recaptchaRequired": (function (){
<#attempt>
<#if passwordRequired??>
return true;
</#if>
<#recover>
</#attempt>
return false;
})(),
"recaptchaSiteKey": (function (){
<#attempt>
return "${recaptchaSiteKey!''}" || undefined;
<#recover>
</#attempt>
})(),
"authorizedMailDomains": (function (){
<#attempt>
return "${authorizedMailDomains!''}" || undefined;
<#recover>
</#attempt>
})(),
"authorizedMailDomains": (function(){
var out = undefined;
<#attempt>
<#if authorizedMailDomains??>
out = [];
<#attempt>
<#list authorizedMailDomains as authorizedMailDomain>
out.push((function (){
<#attempt>
return "${authorizedMailDomain}";
<#recover>
</#attempt>
})());
</#list>
<#recover>
</#attempt>
</#if>
<#recover>
</#attempt>
return out;
})(),
}
</script>

View File

@ -1,3 +1,10 @@
#!/usr/bin/env node
export * from "./build-keycloak-theme";
export * from "./build-keycloak-theme";
import { main } from "./build-keycloak-theme";
if (require.main === module) {
main();
}

View File

@ -120,7 +120,7 @@ export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login
<ul className={cx(props.kcFormSocialAccountListClass, social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass)}>
{
social.providers.map(p =>
<li className={cx(props.kcFormSocialAccountListLinkClass)}>
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
<span>{p.displayName}</span>
</a>

View File

@ -269,7 +269,10 @@ export const Template = memo((props: TemplateProps) => {
{message.type === "warning" && <span className={cx(props.kcFeedbackWarningIcon)}></span>}
{message.type === "error" && <span className={cx(props.kcFeedbackErrorIcon)}></span>}
{message.type === "info" && <span className={cx(props.kcFeedbackInfoIcon)}></span>}
<span className="kc-feedback-text">{message.summary}</span>
<span
className="kc-feedback-text"
dangerouslySetInnerHTML={{ "__html": message.summary }}
/>
</div>
}
{formNode}

View File

@ -9,6 +9,13 @@ import ReactMarkdown from "react-markdown";
export type MessageKey = keyof typeof kcMessages["en"];
/**
* When the language is switched the page is reloaded, this may appear
* as a bug as you might notice that the language successfully switch before
* reload.
* However we need to tell Keycloak that the user have changed the language
* during login so we can retrieve the "local" field of the JWT encoded accessToken.
*/
export function useKcMessage() {
const { kcLanguageTag } = useKcLanguageTag();

View File

@ -130,6 +130,15 @@ export declare namespace KcContext {
* (https://github.com/micedre/keycloak-mail-whitelisting)
*/
authorizedMailDomains?: string[];
social: {
displayInfo: boolean;
providers?: {
loginUrl: string;
alias: string;
providerId: string;
displayName: string;
}[]
};
};
export type Info = Common & {

View File

@ -168,7 +168,10 @@ export const kcRegisterContext: KcContext.Register = {
"*.yet-another-example.com",
"*.example.com",
"hello-world.com"
]
],
"social": {
"displayInfo": true
},
};
export const kcInfoContext: KcContext.Info = {

View File

@ -43,9 +43,9 @@
"@babel/helper-plugin-utils" "^7.14.5"
"@babel/runtime@^7.13.10", "@babel/runtime@^7.7.2":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.5.tgz#665450911c6031af38f81db530f387ec04cd9a98"
integrity sha512-121rumjddw9c3NCQ55KGkyE1h/nzWhU/owjhw0l4mQrkzz4x9SGS1X8gFLraHwX7td3Yo4QTL+qj0NcIzN87BA==
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
dependencies:
regenerator-runtime "^0.13.4"
@ -322,9 +322,9 @@ concat-map@0.0.1:
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
convert-source-map@^1.5.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
dependencies:
safe-buffer "~5.1.1"
@ -770,7 +770,7 @@ micromark@~2.11.0:
debug "^4.0.0"
parse-entities "^2.0.0"
minimal-polyfills@^2.1.5, minimal-polyfills@^2.1.6, minimal-polyfills@^2.2.1:
minimal-polyfills@^2.1.5, minimal-polyfills@^2.1.6:
version "2.2.1"
resolved "https://registry.yarnpkg.com/minimal-polyfills/-/minimal-polyfills-2.2.1.tgz#7249d7ece666d3b4e1ec1c1b8f949eb9d44e2308"
integrity sha512-WLmHQrsZob4rVYf8yHapZPNJZ3sspGa/sN8abuSD59b0FifDEE7HMfLUi24z7mPZqTpBXy4Svp+iGvAmclCmXg==
@ -894,13 +894,12 @@ path@^0.12.7:
util "^0.10.3"
powerhooks@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.1.1.tgz#5ce825d595673a5c6f86b227e0fcaca1972bd90d"
integrity sha512-I8bzTU/uRUyeTkeyAR54g862pwMcAX/ErN3xqdUt7n28riqv93N0Cm900018bNRKBq8ovnb+VxOQ+MXJwhkUOA==
version "0.1.4"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.1.4.tgz#82aae8dae5485a154d3ce4e89342a2d68cb2a413"
integrity sha512-ig47hJIW/b75gCS3l2EtK8NVAduNVd9vem8IvaWkuPuZIP4mbDAy4Rc0/hvjXVUPs9OzK7jc/zIQika3tTacYg==
dependencies:
evt "2.0.0-beta.15"
memoizee "^0.4.15"
minimal-polyfills "^2.2.1"
resize-observer-polyfill "^1.5.1"
tsafe "^0.1.0"
@ -1150,9 +1149,9 @@ type@^2.0.0:
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
typescript@^4.2.3:
version "4.3.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
version "4.3.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==
unified@^9.0.0:
version "9.2.1"
@ -1265,9 +1264,9 @@ yaml@^1.7.2:
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@^20.2.2:
version "20.2.7"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yargs@^16.1.0:
version "16.2.0"